{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 14 go代码命名习惯"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 准备"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `mkdir -p go_project/_go_basic && cd go_project/_go_basic`\n",
    "- `go mod init`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "! mkdir -p 14/go_project/go_basic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "go: /mnt/d/dev_learn/golang_from_beginner_to_mastery/01_basic/14/go_project/go_basic/go.mod already exists\n",
      "exit status 1\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && go mod init go_basic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "module go_basic\n",
      "\n",
      "go 1.24.3\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && cat go.mod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "package main\n",
      "\n",
      "import \"fmt\"\n",
      "\n",
      "func hello_world() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n",
      "\n",
      "func HelloWorld() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n",
      "\n",
      "func main() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && cat hello_world.go"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "VSCode配置好后, 可以点击跳转, `Ctrl+左键`\n",
    "\n",
    "![14.1](./14/images/14.1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && go run 'hello_world.go'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![14.2](./14/images/14.2.png)\n",
    "\n",
    "`go run` 要求被编译的文件属于 **main 包**，也就是文件里需要 `package main` 以及 `func main()` 作为程序入口。你看到的错误说明当前的 `hello_world.go` 不是 main 包，或缺少 `main` 函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "package main\n",
      "\n",
      "import \"fmt\"\n",
      "\n",
      "func hello_world() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n",
      "\n",
      "func HelloWorld() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n",
      "\n",
      "func main() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && cat hello_world.go"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "关于go语言的大括号, 分号\n",
    "- Go 使用大括号来界定作用域，左括号通常需与关键字同行。\n",
    "- 分号大多数情况可省略，依赖编译器自动插入；但在 `for` 循环头部、同一行多语句等场景须显式写出。\n",
    "- `gofmt` 可以自动处理括号与分号的格式问题，建议养成使用习惯。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && go run 'hello_world.go'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "! cd 14/go_project/go_basic && go build -o hello_world"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && ./hello_world"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "go.mod\n",
      "hello_world\n",
      "hello_world.go\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && ls"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `go run .` 的意思是让 Go 编译并运行当前目录下的所有 `.go` 文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello world\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && go run . # "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "VSCode快捷键\n",
    "- `Ctrl+X`: 删除当前行\n",
    "- Terminal中, 上, 重复上次命令\n",
    "- VSCode 自动保存设置: 搜素Auto Save->onFocusChange"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 课程内容"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- https://go.dev/wiki/CodeReviewComments\n",
    "- https://go.dev/doc/effective_go#names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "package main\n",
      "\n",
      "import \"fmt\"\n",
      "\n",
      "func hello_world() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n",
      "\n",
      "func HelloWorld() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n",
      "\n",
      "func main() {\n",
      "\tfmt.Println(\"hello world\")\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "! cd 14/go_project/go_basic && cat hello_world.go"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 15 基础数据类型及其格式化输出"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 一个目录下只有一个`main`函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "package main\n",
      "\n",
      "import \"fmt\"\n",
      "\n",
      "func main() {\n",
      "\tvar MyName int\n",
      "\tfmt.Println(MyName)\n",
      "\tvar a int // 声明整数变量 a，默认零值为 0; int 的宽度与 CPU 的“字长”（word size）一致\n",
      "\tvar b = a // 将 a 的当前值复制给 b（此时也是 0）\n",
      "\t_ = b     // 使用空白标识符避免 “未使用变量” 编译错误\n",
      "\tc := b\n",
      "\ta = c\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "! cd 15/go_project/go_basic && cat primitive.go"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![15.1](./15/images/15.1.png)\n",
    "\n",
    "Golang要求声明后必须使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "! cd 15/go_project/go_basic && go run 'primitive.go'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "package main\n",
      "\n",
      "import \"fmt\"\n",
      "\n",
      "func main() {\n",
      "\tvar MyName int\n",
      "\tfmt.Println(MyName)\n",
      "\tvar a int // 声明整数变量 a，默认零值为 0; int 的宽度与 CPU 的“字长”（word size）一致\n",
      "\tvar b = a // 将 a 的当前值复制给 b（此时也是 0）\n",
      "\t_ = b     // 使用空白标识符避免 “未使用变量” 编译错误\n",
      "\tc := b\n",
      "\ta = c\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "! cd 15/go_project/go_basic && cat primitive.go"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "! cd 15/go_project/go_basic && go run 'primitive.go'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "var a int\n",
    "\n",
    "%%\n",
    "fmt.Println(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "package main\n",
      "\n",
      "import \"fmt\"\n",
      "\n",
      "func main() {\n",
      "\tvar MyName int\n",
      "\tfmt.Println(MyName)\n",
      "\tvar a int // 声明整数变量 a，默认零值为 0; int 的宽度与 CPU 的“字长”（word size）一致\n",
      "\tvar b = a // 将 a 的当前值复制给 b（此时也是 0）\n",
      "\t_ = b     // 使用空白标识符避免 “未使用变量” 编译错误\n",
      "\tc := b\n",
      "\ta = c\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "! cd 15/go_project/go_basic && cat primitive.go"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "! cd 15/go_project/go_basic && go run 'primitive.go'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "package main\n",
      "\n",
      "import \"fmt\"\n",
      "\n",
      "func main() {\n",
      "\tvar MyName int\n",
      "\tfmt.Println(MyName)\n",
      "\tvar a int // 声明整数变量 a，默认零值为 0; int 的宽度与 CPU 的“字长”（word size）一致\n",
      "\tvar b = a // 将 a 的当前值复制给 b（此时也是 0）\n",
      "\t_ = b     // 使用空白标识符避免 “未使用变量” 编译错误\n",
      "\tc := b\n",
      "\ta = c\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "! cd 15/go_project/go_basic && cat primitive.go"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "! cd 15/go_project/go_basic && go run 'primitive.go'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> - `var`：更正式、范围广、适合全局和显式类型表达。\n",
    "> - `:=`：函数内部的快捷声明，强调“根据当前表达式推导、这里新引入一个变量”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "var (\n",
    "\td uint16       // 声明 16 位无符号整数，零值为 0\n",
    "\te int8         // 声明 8 位有符号整数，零值为 0\n",
    "\tf float32      // 32 位浮点数，零值为 0\n",
    "\tg float64      // 64 位浮点数，零值为 0\n",
    "\th = 0o5        // 八进制字面量，推导类型为 int，数值等于 5\n",
    "\ti = 0xa        // 十六进制字面量，推导类型为 int，数值等于 10\n",
    "\tj = 510_123_123 // 十进制字面量，使用下划线分隔以增强可读性，推导类型为 int\n",
    "\tk float64 = 34. // 显式指定 float64，字面量 34. 相当于 34.0\n",
    "\tl bool = true   // 显式指定布尔类型，初始值为 true\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> - https://go.dev/ref/spec#Types"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 分组导入（grouped import）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "import (\n",
    "\t\"math\"\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好，Alice！\n",
      "年龄：30 岁\n",
      "成绩：95.68 分\n",
      "布尔值：true\n",
      "十六进制：0x1E\n",
      "结构体：{X:3 Y:4}\n",
      "类型：main.Point\n",
      "指针地址：0xc000118040\n",
      "原始格式：map[string]int{\"A\":1}\n",
      "f=1234.000000, g=0.000568, g=0.00056789, f=1.234000e+03\n"
     ]
    }
   ],
   "source": [
    "// https://pkg.go.dev/fmt#hdr-Printing\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "type Point struct {\n",
    "\tX, Y int\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tname := \"Alice\"\n",
    "\tage := 30\n",
    "\tscore := 95.6789\n",
    "\tp := Point{X: 3, Y: 4}\n",
    "\n",
    "\tfmt.Printf(\"你好，%s！\\n\", name)                         // %s 字符串\n",
    "\tfmt.Printf(\"年龄：%d 岁\\n\", age)                        // %d 十进制整数\n",
    "\tfmt.Printf(\"成绩：%.2f 分\\n\", score)                    // %.2f 保留两位小数的浮点数\n",
    "\tfmt.Printf(\"布尔值：%t\\n\", age >= 18)                  // %t 布尔值\n",
    "\tfmt.Printf(\"十六进制：0x%X\\n\", age)                    // %X 十六进制（大写）\n",
    "\tfmt.Printf(\"结构体：%+v\\n\", p)                         // %+v 带字段名的结构体\n",
    "\tfmt.Printf(\"类型：%T\\n\", p)                            // %T 输出变量类型\n",
    "\tfmt.Printf(\"指针地址：%p\\n\", &p)                       // %p 指针（十六进制地址）\n",
    "\tfmt.Printf(\"原始格式：%#v\\n\", map[string]int{\"A\": 1}) // %#v Go 语法表示格式\n",
    "\n",
    "\tf := 1234.0\n",
    "\tg := 0.00056789\n",
    "\tfmt.Printf(\"f=%[1]f, g=%[2]f, g=%[2]g, f=%[1]e\\n\", f, g)\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 16 进制练习：Excel一共多少列."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "进制转换\n",
    "\n",
    "![16.1](./16/images/16.1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A=65 Z=90\n",
      "26 进制\n",
      "total 16384\n"
     ]
    }
   ],
   "source": [
    "import \"fmt\"\n",
    "\n",
    "// Excel的最后一列编号是 XFD，请问 Excel 总共多少列？\n",
    "// 这是 26 进制\n",
    "func foo() {\n",
    "\tfmt.Printf(\"A=%d Z=%d\\n\", 'A', 'Z')\n",
    "\tvar base int = 'Z' - 'A' + 1 // 进制\n",
    "\tfmt.Println(base, \"进制\")    // 26\n",
    "\n",
    "\t// 总和\n",
    "\tvar total int\n",
    "\ttotal += 'D' - 'A' + 1\n",
    "\ttotal += base * ('F' - 'A' + 1)\n",
    "\ttotal += base * base * ('X' - 'A' + 1)\n",
    "\tfmt.Println(\"total\", total) // 16384\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      ".\n",
      "├── go.mod\n",
      "└── go_basic\n",
      "    └── hello_world.go\n",
      "\n",
      "1 directory, 2 files\n"
     ]
    }
   ],
   "source": [
    "! cd 16/go_project && tree"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A=65 Z=90\n",
      "26 进制\n",
      "total 16384\n"
     ]
    }
   ],
   "source": [
    "! cd 16/go_project && go run ./go_basic"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 17 go代码注释书写规范"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      ".\n",
      "└── go_basic\n",
      "    ├── go.mod\n",
      "    ├── main.go\n",
      "    └── mathutil\n",
      "        ├── divide.go\n",
      "        └── doc.go\n",
      "\n",
      "2 directories, 4 files\n"
     ]
    }
   ],
   "source": [
    "! cd 17/go_project && tree"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "! cd 17/go_project/go_basic && go run ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 简要说明"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. 包注释（Package Comment）\n",
    "\n",
    "- 写在 `package` 声明之前。\n",
    "- 第一行最好以 “Package 包名 ...” 开头。\n",
    "- 简要说明包的用途，通常用于 `godoc` 文档。\n",
    "\n",
    "```go\n",
    "// Package calc 提供基本的算术运算功能。\n",
    "package calc\n",
    "```\n",
    "\n",
    "### 2. 导出标识符注释（Exported Identifier Comment）\n",
    "\n",
    "- 所有导出的类型、函数、变量、常量都需要注释。\n",
    "- 注释的第一句应以标识符的名字开头。\n",
    "- 说明这个标识符的用途或行为。\n",
    "\n",
    "\n",
    "```go\n",
    "// Add 返回 a 和 b 的和。\n",
    "func Add(a, b int) int {\n",
    "\treturn a + b\n",
    "}\n",
    "\n",
    "// ErrOverflow 在发生整型溢出时返回。\n",
    "var ErrOverflow = errors.New(\"overflow\")\n",
    "```\n",
    "\n",
    "### 3. 行内注释与块注释\n",
    "\n",
    "- 尽量使用 `//` 行注释，简洁明了。\n",
    "- 对复杂逻辑可以使用块注释 `/* ... */`，但要避免嵌套。\n",
    "- 行内注释与代码之间保留一个空格。\n",
    "\n",
    "\n",
    "```go\n",
    "for i := 0; i < len(nums); i++ {\n",
    "\tif nums[i] < 0 {\n",
    "\t\t// 遇到负数就提前停止。\n",
    "\t\tbreak\n",
    "\t}\n",
    "\tsum += nums[i]\n",
    "}\n",
    "```\n",
    "\n",
    "\n",
    "### 4. 示例：结合以上规范\n",
    "\n",
    "\n",
    "```go\n",
    "// Package mathutil 提供简单的数学工具函数。\n",
    "package mathutil\n",
    "\n",
    "import \"errors\"\n",
    "\n",
    "// Divide 返回 a 除以 b 的商。\n",
    "// 当 b 为 0 时，返回 ErrDivideByZero。\n",
    "func Divide(a, b float64) (float64, error) {\n",
    "\tif b == 0 {\n",
    "\t\treturn 0, ErrDivideByZero\n",
    "\t}\n",
    "\treturn a / b, nil\n",
    "}\n",
    "\n",
    "// ErrDivideByZero 表示除以零的错误。\n",
    "var ErrDivideByZero = errors.New(\"divide by zero\")\n",
    "```\n",
    "\n",
    "### 小结\n",
    "\n",
    "1. 注释应以被注释对象的名称开头。\n",
    "2. 使用完整句子（首字母大写，句末加句号(英文句号)），便于生成文档。\n",
    "3. 面向外部使用者的对象必须有注释，否则 `golint` 会提示。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 18 常量与枚举"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "App: ExampleApp\n",
      "Pi: 3.14159\n",
      "StatusPending: 0\n",
      "StatusProcessing: 1\n",
      "StatusDone: 2\n"
     ]
    }
   ],
   "source": [
    "import \"fmt\"\n",
    "\n",
    "// 定义独立常量\n",
    "const (\n",
    "\tPi      = 3.14159\n",
    "\tAppName = \"ExampleApp\"\n",
    ")\n",
    "\n",
    "// 定义枚举型常量（使用 iota 自动递增）\n",
    "const (\n",
    "\tStatusPending = iota // 0\n",
    "\tStatusProcessing     // 1\n",
    "\tStatusDone           // 2\n",
    ")\n",
    "\n",
    "cost\n",
    "\n",
    "func foo() {\n",
    "\tfmt.Println(\"App:\", AppName)\n",
    "\tfmt.Println(\"Pi:\", Pi)\n",
    "\n",
    "\tfmt.Println(\"StatusPending:\", StatusPending)\n",
    "\tfmt.Println(\"StatusProcessing:\", StatusProcessing)\n",
    "\tfmt.Println(\"StatusDone:\", StatusDone)\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```go\n",
    "const (\n",
    "\tNOT_USE = 1 << (10 * iota) // iota == 0 → 1\n",
    "\tKB      = 1 << (10 * iota) // iota == 1 → 1024\n",
    "\tMB      = 1 << (10 * iota) // iota == 2 → 1,048,576\n",
    "\tGB      = 1 << (10 * iota) // iota == 3 → 1,073,741,824\n",
    ")\n",
    "```\n",
    "\n",
    "```go\n",
    "const (\n",
    "\tNOT_USE = 1 << (10 * iota)  // iota == 0 → 1 << 0  == 1\n",
    "\tKB                          // iota == 1 → 1 << 10 == 1024\n",
    "\tMB                          // iota == 2 → 1 << 20 == 1,048,576\n",
    "\tGB                          // iota == 3 → 1 << 30 == 1,073,741,824\n",
    ")\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NOT_USE: 1\n",
      "KB: 1024\n",
      "MB: 1048576\n",
      "GB: 1073741824\n"
     ]
    }
   ],
   "source": [
    "const (\n",
    "\t\t\tNOT_USE = 1 << (10 * iota)\n",
    "\t\t\tKB\n",
    "\t\t\tMB\n",
    "\t\t\tGB\n",
    ")\n",
    "\n",
    "%%\n",
    "fmt.Println(\"NOT_USE:\", NOT_USE) // 1\n",
    "fmt.Println(\"KB:\", KB)           // 1024\n",
    "fmt.Println(\"MB:\", MB)           // 1048576\n",
    "fmt.Println(\"GB:\", GB)           // 1073741824"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 3\n",
      "4 5\n",
      "6 7\n"
     ]
    }
   ],
   "source": [
    "import \"fmt\"\n",
    "\n",
    "const (\n",
    "\tss, mm = iota + 1, iota + 2\n",
    "\tcc, dd\n",
    "\tff, nn\n",
    ")\n",
    "\n",
    "%%\n",
    "fmt.Println(ss, mm)\n",
    "fmt.Println(cc, dd)\n",
    "fmt.Println(ff, nn)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 19 结构体及其指针"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 结构体的 **值语义**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 函数接收的是拷贝，修改不会回写到调用者"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "《The Go Programming Language》 by Alan A. A. Donovan, 380 pages\n",
      "原始页数: 380\n"
     ]
    }
   ],
   "source": [
    "import \"fmt\"\n",
    "\n",
    "type Book struct {\n",
    "\tTitle  string\n",
    "\tAuthor string\n",
    "\tPages  int\n",
    "}\n",
    "\n",
    "func printAndTryModify(b Book) {\n",
    "\tfmt.Printf(\"《%s》 by %s, %d pages\\n\", b.Title, b.Author, b.Pages)\n",
    "\tb.Pages = 999 // 仅修改副本\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tbook := Book{\n",
    "\t\tTitle:  \"The Go Programming Language\",\n",
    "\t\tAuthor: \"Alan A. A. Donovan\",\n",
    "\t\tPages:  380,\n",
    "\t}\n",
    "\n",
    "\tprintBookInfo(book) // 传入的是 book 的副本\n",
    "\tfmt.Println(\"原始页数:\", book.Pages) // 380，未被修改\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 结构体的 **引用语义**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 通过指针实现的**引用语义**，适用于需要原地修改数据的场景"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Deposited 50.00 into Alice's account.\n",
      "最新余额: 150\n"
     ]
    }
   ],
   "source": [
    "import \"fmt\"\n",
    "\n",
    "type Account struct {\n",
    "\tOwner  string\n",
    "\tBalance float64\n",
    "}\n",
    "\n",
    "// 接受结构体指针的函数：可直接修改账户\n",
    "func deposit(acc *Account, amount float64) {\n",
    "\tacc.Balance += amount\n",
    "\tfmt.Printf(\"Deposited %.2f into %s's account.\\n\", amount, acc.Owner)\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tacc := &Account{\n",
    "\t\tOwner:  \"Alice\",\n",
    "\t\tBalance: 100.0,\n",
    "\t}\n",
    "\n",
    "\tdeposit(acc, 50.0)             // 传入指针\n",
    "\tfmt.Println(\"最新余额:\", acc.Balance) // 150，原值已被修改\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 匿名结构体"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Go 的匿名结构体（anonymous struct）指的是不先用 `type` 关键字给结构体命名，而是直接在需要的位置内联定义并使用。这种写法常用于临时数据结构、快速组合字段或在测试中构造输入输出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Alice is 30 years old.\n"
     ]
    }
   ],
   "source": [
    "// 1. 单个匿名结构体变量\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func foo() {\n",
    "\t// 内联定义并初始化\n",
    "\tperson := struct {\n",
    "\t\tName string\n",
    "\t\tAge  int\n",
    "\t}{\n",
    "\t\tName: \"Alice\",\n",
    "\t\tAge:  30,\n",
    "\t}\n",
    "\n",
    "\tfmt.Printf(\"%s is %d years old.\\n\", person.Name, person.Age)\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "200 OK\n"
     ]
    }
   ],
   "source": [
    "// 2. 函数返回匿名结构体\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func getStatus() struct {\n",
    "\tCode    int\n",
    "\tMessage string\n",
    "} {\n",
    "\treturn struct {\n",
    "\t\tCode    int\n",
    "\t\tMessage string\n",
    "\t}{\n",
    "\t\tCode:    200,\n",
    "\t\tMessage: \"OK\",\n",
    "\t}\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tstatus := getStatus()\n",
    "\tfmt.Println(status.Code, status.Message) // 200 OK\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 a@example.com\n",
      "2 b@example.com\n"
     ]
    }
   ],
   "source": [
    "// 3. 匿名结构体切片 / 映射\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func foo() {\n",
    "\tusers := []struct {\n",
    "\t\tID    int\n",
    "\t\tEmail string\n",
    "\t}{\n",
    "\t\t{ID: 1, Email: \"a@example.com\"},\n",
    "\t\t{ID: 2, Email: \"b@example.com\"},\n",
    "\t}\n",
    "\n",
    "\tfor _, u := range users {\n",
    "\t\tfmt.Println(u.ID, u.Email)\n",
    "\t}\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 值接收者方法, 指针接收者方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 值接收者方法 展示不会修改原始数据。\n",
    "- 指针接收者方法 展示如何原地修改结构体字段。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hi, I'm Alice and I'm 29 years old.\n",
      "Happy birthday, Alice! You are now 30.\n",
      "Hi, I'm Bob and I'm 40 years old.\n",
      "Happy birthday, Bob! You are now 41.\n",
      "41\n",
      "Hi, I'm Charlie and I'm 25 years old.\n",
      "Happy birthday, Charlie! You are now 26.\n",
      "26\n"
     ]
    }
   ],
   "source": [
    "import \"fmt\"\n",
    "\n",
    "// Person 表示一个人。\n",
    "type Person struct {\n",
    "\tName string\n",
    "\tAge  int\n",
    "}\n",
    "\n",
    "// Greet 是值接收者方法：不会修改 Person 原值。\n",
    "func (p Person) Greet() {\n",
    "\tfmt.Printf(\"Hi, I'm %s and I'm %d years old.\\n\", p.Name, p.Age)\n",
    "}\n",
    "\n",
    "// HaveBirthday 是指针接收者方法：可以修改 Person 原值。\n",
    "func (p *Person) HaveBirthday() {\n",
    "\tp.Age++\n",
    "\tfmt.Printf(\"Happy birthday, %s! You are now %d.\\n\", p.Name, p.Age)\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\talice := Person{Name: \"Alice\", Age: 29}\n",
    "\n",
    "\t// 值方法：调用时会复制 alice 的值\n",
    "\talice.Greet() // Hi, I'm Alice and I'm 29 years old.\n",
    "\n",
    "\t// 指针方法：传入地址，可直接修改 alice.Age\n",
    "\talice.HaveBirthday() // Happy birthday, Alice! You are now 30.\n",
    "\n",
    "\t// 手动获取指针并修改\n",
    "\tbob := &Person{Name: \"Bob\", Age: 40} // 直接创建了结构体指针；调用方法时无需解引用，编译器会自动处理\n",
    "\tbob.Greet()         // Hi, I'm Bob and I'm 40 years old.\n",
    "\tbob.HaveBirthday()  // Happy birthday, Bob! You are now 41.\n",
    "\tfmt.Println(bob.Age) // 41（已被修改）\n",
    "\n",
    "\t// 使用 new 关键字创建零值指针\n",
    "\tcharlie := new(Person) // *Person，字段为零值\n",
    "\tcharlie.Name = \"Charlie\"\n",
    "\tcharlie.Age = 25\n",
    "\tcharlie.Greet()        // Hi, I'm Charlie and I'm 25 years old.\n",
    "\tcharlie.HaveBirthday() // Happy birthday, Charlie! You are now 26.\n",
    "\tfmt.Println(charlie.Age) // 26\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 结构体赋值为空, 以及new"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "type User struct {\n",
    "    Name    string\n",
    "    Address string\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "    u2 := User{}\n",
    "    u2.Address = \"上海\" // ✅ 正确\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 20 继承的实现与应用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 里没有传统面向对象语言（如 Java、C++）里的“类继承”机制，但通过 **结构体嵌入（embedding）** 和 **接口（interface）** 可以实现类似的代码复用与多态效果。关键理念是“组合优于继承”。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 优先使用组合：Go 倡导组合，小组件组合成大组件，而不是复杂继承层级。\n",
    "- 接口尽量小：创建小而精的接口（如单方法接口），方便实现和重用。\n",
    "- 清晰初始化：嵌入结构体时，可以显式初始化嵌入字段，让依赖关系一目了然。\n",
    "- 避免滥用嵌入：虽然方便，但过多嵌入会使结构体关系难以追踪，保持层级简单。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 常见应用场景\n",
    "\n",
    "| 场景           | Go 的实践方式                                       |\n",
    "| -------------- | --------------------------------------------------- |\n",
    "| 代码复用       | 使用结构体嵌入，把公共字段/方法放在“基类”结构体中。 |\n",
    "| 多态行为       | 定义接口，嵌入或组合结构体使其实现接口。            |\n",
    "| 方法覆盖       | 在外层结构体重新定义同名方法。                      |\n",
    "| 构造更复杂类型 | 层层嵌入，类似继承树，但本质是组合。                |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 结构体嵌入（Struct Embedding）\n",
    "\n",
    "结构体中嵌入另一个类型（通常是结构体）时，可以把被嵌入类型的字段与方法“提升”（promote）到外层，使得外层类型像是在“继承”内层的行为。\n",
    "- `Dog` 结构体嵌入 `Animal`，从而“继承”了 `Animal` 的字段和方法。\n",
    "- 可以在 `Dog` 上定义同名方法来覆盖默认行为。\n",
    "- 嵌入不仅限于结构体，可以嵌入任意类型（包括接口），但最常见的用法是结构体。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Buddy the Golden Retriever barks.\n",
      "Buddy makes a sound.\n"
     ]
    }
   ],
   "source": [
    "type Animal struct {\n",
    "\tName string\n",
    "}\n",
    "\n",
    "func (a Animal) Speak() {\n",
    "\tfmt.Printf(\"%s makes a sound.\\n\", a.Name)\n",
    "}\n",
    "\n",
    "type Dog struct {\n",
    "\tAnimal // 嵌入\n",
    "\tBreed  string\n",
    "}\n",
    "\n",
    "func (d Dog) Speak() { // 方法重写\n",
    "\tfmt.Printf(\"%s the %s barks.\\n\", d.Name, d.Breed)\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\td := Dog{\n",
    "\t\tAnimal: Animal{Name: \"Buddy\"},\n",
    "\t\tBreed:  \"Golden Retriever\",\n",
    "\t}\n",
    "\n",
    "\td.Speak()      // 使用 Dog 自己的 Speak\n",
    "\td.Animal.Speak() // 调用被“覆盖”的 Animal.Speak\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 接口与多态（Interface Polymorphism）\n",
    "\n",
    "Go 的接口是隐式实现的：只要类型实现了接口所需的方法，就认为它满足该接口。结合结构体嵌入，可以构建灵活的“继承”风格体系。\n",
    "- `Dog` 和 `Cat` 都有 `Speak()` 方法，因此自动实现 `Speaker`。\n",
    "- `Announce` 函数对所有实现 `Speaker` 的类型通用，实现运行时多态。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "type Speaker interface {\n",
    "\tSpeak()\n",
    "}\n",
    "\n",
    "func Announce(s Speaker) {\n",
    "\ts.Speak()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "现在，可以让多个结构体实现 `Speak()` 方法，自动满足 `Speaker` 接口。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Buddy the Golden Retriever barks.\n",
      "Kitty the cat meows.\n"
     ]
    }
   ],
   "source": [
    "type Cat struct {\n",
    "\tAnimal\n",
    "}\n",
    "\n",
    "func (c Cat) Speak() {\n",
    "\tfmt.Printf(\"%s the cat meows.\\n\", c.Name)\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tdog := Dog{Animal: Animal{Name: \"Buddy\"}, Breed: \"Golden Retriever\"}\n",
    "\tcat := Cat{Animal: Animal{Name: \"Kitty\"}}\n",
    "\n",
    "\tAnnounce(dog)\n",
    "\tAnnounce(cat)\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 嵌入与接口结合实现“继承链”\n",
    "\n",
    "通过嵌入，可以模拟“父类 → 子类 → 孙类”式的分层。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tesla electric car starts silently. Battery: 75 kWh\n",
      "Tesla vehicle starts.\n",
      "Tesla vehicle starts.\n"
     ]
    }
   ],
   "source": [
    "type Vehicle struct {\n",
    "\tBrand string\n",
    "}\n",
    "\n",
    "func (v Vehicle) Start() {\n",
    "\tfmt.Printf(\"%s vehicle starts.\\n\", v.Brand)\n",
    "}\n",
    "\n",
    "type Car struct {\n",
    "\tVehicle\n",
    "\tDoors int\n",
    "}\n",
    "\n",
    "type ElectricCar struct {\n",
    "\tCar\n",
    "\tBatteryCapacity int\n",
    "}\n",
    "\n",
    "func (ec ElectricCar) Start() {\n",
    "\tfmt.Printf(\"%s electric car starts silently. Battery: %d kWh\\n\",\n",
    "\t\tec.Brand, ec.BatteryCapacity)\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\ttesla := ElectricCar{\n",
    "\t\tCar: Car{\n",
    "\t\t\tVehicle: Vehicle{Brand: \"Tesla\"},\n",
    "\t\t\tDoors:   4,\n",
    "\t\t},\n",
    "\t\tBatteryCapacity: 75,\n",
    "\t}\n",
    "\n",
    "\ttesla.Start()        // ElectricCar.Start\n",
    "\ttesla.Car.Start()    // Vehicle.Start 通过 Car\n",
    "\ttesla.Vehicle.Start() // 直接调用最底层\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 21 结构体嵌套的几种形式和场景"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 形式             | 特点                     | 典型用途                  |\n",
    "| ---------------- | ------------------------ | ------------------------- |\n",
    "| 命名字段         | 正常组合，访问需带字段名 | 模型化实体关系、JSON 映射 |\n",
    "| 匿名嵌入（值）   | 字段/方法提升，可覆盖    | 代码复用、模拟继承        |\n",
    "| 匿名嵌入（指针） | 避免拷贝，可按需初始化   | 大对象组合、装饰模式      |\n",
    "| 匿名结构体       | 快速定义局部结构         | 配置、临时数据            |\n",
    "| 嵌入接口         | 组合接口能力             | 多态适配、统一封装        |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 普通命名字段：经典组合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Shanghai\n",
      "{Name:Alice Age:30 Address:{City:Shanghai Street:Nanjing Rd}}\n",
      "main.Person{Name:\"Alice\", Age:30, Address:main.Address{City:\"Shanghai\", Street:\"Nanjing Rd\"}}\n"
     ]
    }
   ],
   "source": [
    "// 适合表示“拥有”关系（has-a），字段名清晰，初始化时必须显式填写。\n",
    "// 简单组合、JSON 映射、将对象拆分为多个逻辑部分等\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Address struct {\n",
    "\tCity  string\n",
    "\tStreet string\n",
    "}\n",
    "\n",
    "type Person struct {\n",
    "\tName    string\n",
    "\tAge     int\n",
    "\tAddress Address // 命名字段\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tp := Person{\n",
    "\t\tName: \"Alice\",\n",
    "\t\tAge:  30,\n",
    "\t\tAddress: Address{\n",
    "\t\t\tCity:   \"Shanghai\",\n",
    "\t\t\tStreet: \"Nanjing Rd\",\n",
    "\t\t},\n",
    "\t}\n",
    "\n",
    "\tfmt.Println(p.Address.City) // Shanghai\n",
    "\tfmt.Printf(\"%+v\\n\", p)\n",
    "\tfmt.Printf(\"%#v\\n\", p)\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 匿名字段嵌入（值）：字段/方法“提升”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Buddy the Golden Retriever barks.\n",
      "Buddy makes a sound.\n"
     ]
    }
   ],
   "source": [
    "// 嵌入类型本身（无字段名）会将其导出字段和方法提升到外层，就像“继承”。\n",
    "// 模拟继承、代码复用、对外暴露“基类”字段和方法\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Animal struct {\n",
    "\tName string\n",
    "}\n",
    "\n",
    "func (a Animal) Speak() {\n",
    "\tfmt.Printf(\"%s makes a sound.\\n\", a.Name)\n",
    "}\n",
    "\n",
    "type Dog struct {\n",
    "\tAnimal       // 匿名嵌入\n",
    "\tBreed  string\n",
    "}\n",
    "\n",
    "func (d Dog) Speak() { // 方法覆盖\n",
    "\tfmt.Printf(\"%s the %s barks.\\n\", d.Name, d.Breed)\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\td := Dog{\n",
    "\t\tAnimal: Animal{Name: \"Buddy\"},\n",
    "\t\tBreed:  \"Golden Retriever\",\n",
    "\t}\n",
    "\n",
    "\td.Speak()        // Buddy the Golden Retriever barks.\n",
    "\td.Animal.Speak() // Buddy makes a sound.\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 匿名字段嵌入（指针）：避免拷贝、可选组合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[OrderService] started\n"
     ]
    }
   ],
   "source": [
    "// 嵌入指针可减少拷贝，字段可为空（nil）。常用于大型结构或延迟初始化。\n",
    "// 装饰器、可选依赖、可重用基础设施组件\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Logger struct {\n",
    "\tPrefix string\n",
    "}\n",
    "\n",
    "func (l *Logger) Print(msg string) {\n",
    "\tfmt.Printf(\"[%s] %s\\n\", l.Prefix, msg)\n",
    "}\n",
    "\n",
    "type Service struct {\n",
    "\t*Logger // 指针嵌入\n",
    "\tName    string\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\ts := Service{Name: \"OrderService\"}\n",
    "\n",
    "\tif s.Logger == nil {\n",
    "\t\ts.Logger = &Logger{Prefix: s.Name}\n",
    "\t}\n",
    "\n",
    "\ts.Print(\"started\") // [OrderService] started\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 匿名结构体（inline struct）：临时聚合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{postgres://user:pass@localhost/db 10}\n"
     ]
    }
   ],
   "source": [
    "// 在字段声明时直接定义匿名结构体，简化只在局部使用的数据结构。\n",
    "// 配置对象、测试用临时结构、不希望在包外导出单独类型。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Config struct {\n",
    "\tDB struct {\n",
    "\t\tDSN    string\n",
    "\t\tMaxCon int\n",
    "\t}\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tcfg := Config{}\n",
    "\tcfg.DB.DSN = \"postgres://user:pass@localhost/db\"\n",
    "\tcfg.DB.MaxCon = 10\n",
    "\n",
    "\tfmt.Println(cfg.DB) // {postgres://user:pass@localhost/db 10}\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. 嵌入接口：组合多种能力"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello\n"
     ]
    }
   ],
   "source": [
    "// Go 接口也可作为匿名字段嵌入，方便组合多种行为。例如同时具备读写能力。\n",
    "// 场景：封装第三方库、构建具备多接口能力的适配器。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"bytes\"\n",
    "\t\"fmt\"\n",
    "\t\"io\"\n",
    ")\n",
    "\n",
    "type ReadWriter struct {\n",
    "\tio.Reader // 匿名接口嵌入\n",
    "\tio.Writer\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tvar buf bytes.Buffer\n",
    "\trw := ReadWriter{\n",
    "\t\tReader: &buf,\n",
    "\t\tWriter: &buf,\n",
    "\t}\n",
    "\n",
    "\trw.Write([]byte(\"hello\"))\n",
    "\tdata := make([]byte, 5)\n",
    "\trw.Read(data)\n",
    "\n",
    "\tfmt.Println(string(data)) // hello\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 结构体“循环引用”与自引用指针成员"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 不能直接把结构体自身作为值字段（会编译失败）。\n",
    "- 可以通过指针、切片、数组字段安全地建立自引用或循环引用。\n",
    "- 常见应用：链表/树、层级结构、父子关系、关联对象互指。\n",
    "- 编写时注意初始化、判空、以及序列化/资源管理中的环依赖处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. 结构体字段不能是自身类型的值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<style>\n",
       ".gonb-err-location {\n",
       "\tbackground: var(--jp-err-color2);  \n",
       "\tborder-radius: 3px;\n",
       "\tborder-style: dotted;\n",
       "\tborder-width: 1px;\n",
       "\tborder-color: var(--jp-border-color2);\n",
       "}\n",
       ".gonb-err-location:hover {\n",
       "\tborder-width: 2px;\n",
       "\tborder-style: solid;\n",
       "\tborder-color: var(--jp-border-color2);\n",
       "}\n",
       ".gonb-err-context {\n",
       "\tdisplay: none;\n",
       "}\n",
       ".gonb-err-location:hover + .gonb-err-context {\n",
       "\tbackground: var(--jp-dialog-background);  \n",
       "\tborder-radius: 3px;\n",
       "\tborder-style: solid;\n",
       "\tborder-width: 1px;\n",
       "\tborder-color: var(--jp-border-color2);\n",
       "\tdisplay: block;\n",
       "\twhite-space: pre;\n",
       "\tfont-family: monospace;\n",
       "}\n",
       ".gonb-err-line {\n",
       "\tborder-radius: 3px;\n",
       "\tborder-style: dotted;\n",
       "\tborder-width: 1px;\t\n",
       "\tborder-color: var(--jp-border-color2);\n",
       "\tbackground-color: var(--jp-rendermime-err-background);\n",
       "\tfont-weight: bold;\n",
       "}\n",
       ".gonb-cell-line-info {\n",
       "\tbackground: var(--jp-layout-color2);\n",
       "\tcolor: #999;\n",
       "\tmargin: 0.1em;\n",
       "\tborder: 1px solid var(--jp-border-color1);\n",
       "\tpadding-left: 0.2em;\n",
       "\tpadding-right: 0.2em;\n",
       "}\n",
       "</style>\n",
       "<div class=\"lm-Widget p-Widget lm-Panel p-Panel jp-OutputArea-child\">\n",
       "<div class=\"lm-Widget p-Widget jp-RenderedText jp-mod-trusted jp-OutputArea-output\" data-mime-type=\"application/vnd.jupyter.stderr\" style=\"font-family: monospace;\">\n",
       "\n",
       "\n",
       "<span style=\"white-space: pre;\"> # gonb_b0774bdc</span>\n",
       "\n",
       "<br/>\n",
       "\n",
       "<span class=\"gonb-cell-line-info\">Cell[29]: Line 3</span>\n",
       "<span class=\"gonb-err-location\">./main.go:49:6: </span> invalid recursive type: Node refers to itself\n",
       "<div class=\"gonb-err-context\">\n",
       "type Logger struct {\n",
       "\tPrefix string\n",
       "}\n",
       "<div class=\"gonb-err-line\">type Node struct {\n",
       "</div>\tValue int\n",
       "\tNext  Node // ❌ 编译错误：Node 自身的值类型字段\n",
       "\n",
       "</div>\n",
       "\n",
       "<br/>\n",
       "\n",
       "\n",
       "<span style=\"white-space: pre;\"> </span>\n",
       "\n",
       "<br/>\n",
       "\n",
       "</div>\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "ename": "ERROR",
     "evalue": "failed to run \"/usr/local/go/bin/go build -o /tmp/gonb_b0774bdc/gonb_b0774bdc\": exit status 1",
     "output_type": "error",
     "traceback": [
      "failed to run \"/usr/local/go/bin/go build -o /tmp/gonb_b0774bdc/gonb_b0774bdc\": exit status 1"
     ]
    }
   ],
   "source": [
    "// 如果尝试让结构体包含一个同类型字段（值语义），会导致编译错误：因为编译器无法确定结构体的最终尺寸——嵌套会无限展开。\n",
    "\n",
    "type Node struct {\n",
    "\tValue int\n",
    "\tNext  Node // ❌ 编译错误：Node 自身的值类型字段\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. 正确做法：使用指针、切片或数组"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "将字段声明为指针或切片，可以形成自引用或循环，因为指针大小固定，不会导致无限递归。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 指针字段：链表、树等典型结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "n1 addr=0xc00000e018 Value=1 Next=0xc00000e030 Prev=0x0\n",
      "n2 addr=0xc00000e030 Value=2 Next=0x0 Prev=0xc00000e018\n"
     ]
    }
   ],
   "source": [
    "// 适合的场景：链表、树（Left/Right/Parent 指针）、图结构等。\n",
    "\n",
    "type Node struct {\n",
    "\tValue int\n",
    "\tNext  *Node    // 指向下一个节点，可为 nil\n",
    "\tPrev  *Node    // 指向上一个节点\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tn1 := &Node{Value: 1}\n",
    "\tn2 := &Node{Value: 2}\n",
    "\n",
    "\tn1.Next = n2\n",
    "\tn2.Prev = n1\n",
    "\n",
    "\tfmt.Printf(\"n1 addr=%p Value=%d Next=%p Prev=%p\\n\", n1, n1.Value, n1.Next, n1.Prev)\n",
    "\tfmt.Printf(\"n2 addr=%p Value=%d Next=%p Prev=%p\\n\", n2, n2.Value, n2.Next, n2.Prev)\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 切片字段：聚合多个同类节点"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "- 1\n",
      "  - 2\n",
      "  - 3\n",
      "    - 5\n",
      "    - 6\n",
      "  - 4\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"strings\"\n",
    ")\n",
    "\n",
    "type TreeNode struct {\n",
    "\tValue    int\n",
    "\tChildren []*TreeNode // 子节点切片: 允许一个节点拥有任意数量的子节点\n",
    "}\n",
    "\n",
    "// AddChild 创建子节点并追加到 Children 切片中，返回新子节点，便于链式操作。\n",
    "func (n *TreeNode) AddChild(value int) *TreeNode {\n",
    "\tchild := &TreeNode{Value: value}\n",
    "\tn.Children = append(n.Children, child)\n",
    "\treturn child\n",
    "}\n",
    "\n",
    "// PrintTree 递归打印整棵树，indent 控制缩进层级。\n",
    "func PrintTree(node *TreeNode, depth int) {\n",
    "\tif node == nil {\n",
    "\t\treturn\n",
    "\t}\n",
    "\tprefix := strings.Repeat(\"  \", depth)\n",
    "\tfmt.Printf(\"%s- %d\\n\", prefix, node.Value)\n",
    "\tfor _, child := range node.Children {\n",
    "\t\tPrintTree(child, depth+1)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\t// 构建一棵简单的树：\n",
    "\t//         1\n",
    "\t//      /  |  \\\n",
    "\t//     2   3   4\n",
    "\t//        / \\\n",
    "\t//       5   6\n",
    "\troot := &TreeNode{Value: 1}\n",
    "\n",
    "\tnode2 := root.AddChild(2)\n",
    "\t_ = node2 // 这里暂未继续添加子节点，可按需扩展\n",
    "\n",
    "\tnode3 := root.AddChild(3)\n",
    "\tnode3.AddChild(5)\n",
    "\tnode3.AddChild(6)\n",
    "\n",
    "\troot.AddChild(4)\n",
    "\n",
    "\tPrintTree(root, 0)\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. 自引用指针成员：表达层级或“父节点”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "- Alice (top manager)\n",
      "  - Bob (manager: Alice)\n",
      "    - Dan (manager: Bob)\n",
      "    - Emma (manager: Bob)\n",
      "  - Carla (manager: Alice)\n",
      "\n",
      "Bob -> Manager: Alice, Reports: 2\n",
      "Dan -> Manager: Bob, Reports: 0\n"
     ]
    }
   ],
   "source": [
    "// 常见例子：员工与经理、文件系统目录、UI 组件层级。\n",
    "// 使用时注意：顶层节点的 Manager 通常为 nil，需在代码中做判空。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Employee struct {\n",
    "\tName    string\n",
    "\tManager *Employee\n",
    "\tReports []*Employee\n",
    "}\n",
    "\n",
    "// assignManager 将 subordinate 的 Manager 设为 manager，并把 subordinate 加入 manager 的 Reports。\n",
    "func assignManager(manager, subordinate *Employee) {\n",
    "\tif manager == nil || subordinate == nil {\n",
    "\t\treturn\n",
    "\t}\n",
    "\tsubordinate.Manager = manager\n",
    "\tmanager.Reports = append(manager.Reports, subordinate)\n",
    "}\n",
    "\n",
    "// printOrg 递归打印组织结构树。\n",
    "func printOrg(e *Employee, depth int) {\n",
    "\tif e == nil {\n",
    "\t\treturn\n",
    "\t}\n",
    "\tprefix := \"\"\n",
    "\tfor i := 0; i < depth; i++ {\n",
    "\t\tprefix += \"  \"\n",
    "\t}\n",
    "\tif e.Manager == nil {\n",
    "\t\tfmt.Printf(\"%s- %s (top manager)\\n\", prefix, e.Name)\n",
    "\t} else {\n",
    "\t\tfmt.Printf(\"%s- %s (manager: %s)\\n\", prefix, e.Name, e.Manager.Name)\n",
    "\t}\n",
    "\tfor _, report := range e.Reports {\n",
    "\t\tprintOrg(report, depth+1)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\t// 创建员工\n",
    "\talice := &Employee{Name: \"Alice\"} // 顶级经理\n",
    "\tbob := &Employee{Name: \"Bob\"}\n",
    "\tcarla := &Employee{Name: \"Carla\"}\n",
    "\tdan := &Employee{Name: \"Dan\"}\n",
    "\temma := &Employee{Name: \"Emma\"}\n",
    "\n",
    "\t// 建立汇报关系\n",
    "\tassignManager(alice, bob)   // Bob 向 Alice 汇报\n",
    "\tassignManager(alice, carla) // Carla 向 Alice 汇报\n",
    "\tassignManager(bob, dan)     // Dan 向 Bob 汇报\n",
    "\tassignManager(bob, emma)    // Emma 向 Bob 汇报\n",
    "\n",
    "\t// 打印组织结构\n",
    "\tprintOrg(alice, 0)\n",
    "\n",
    "\t// 查阅某个员工的上下级信息\n",
    "\tfmt.Println()\n",
    "\tfmt.Printf(\"%s -> Manager: %v, Reports: %d\\n\", bob.Name, bob.Manager.Name, len(bob.Reports))\n",
    "\tfmt.Printf(\"%s -> Manager: %v, Reports: %d\\n\", dan.Name, dan.Manager.Name, len(dan.Reports))\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. 结构体间的循环引用（互相指向）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Backend\n",
      "Alice\n"
     ]
    }
   ],
   "source": [
    "// 两个或多个结构体可以互相包含彼此的指针字段，形成引用环。依然要用指针避免无限大小。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Team struct {\n",
    "\tName      string\n",
    "\tLead      *Person\n",
    "\tMembers   []*Person\n",
    "}\n",
    "\n",
    "type Person struct {\n",
    "\tName string\n",
    "\tTeam *Team\n",
    "}\n",
    "\n",
    "func foo() {\n",
    "\tt := &Team{Name: \"Backend\"}\n",
    "\tp := &Person{Name: \"Alice\"}\n",
    "\n",
    "\tt.Lead = p\n",
    "\tt.Members = []*Person{p}\n",
    "\n",
    "\tp.Team = t\n",
    "\n",
    "\tfmt.Println(p.Team.Name) // Backend\n",
    "\tfmt.Println(t.Lead.Name) // Alice\n",
    "}\n",
    "\n",
    "%%\n",
    "foo()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 注意事项与实践建议"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. **零值安全**：指针字段初始为 `nil`，使用前要判空或在构造函数里初始化。\n",
    "2. **避免过多环**：虽然 Go 的 GC 能处理循环引用，但仍需谨慎，特别是在手动管理资源（如关闭文件、网络连接）时，确保能够释放。\n",
    "3. **构造函数封装**：提供函数初始化复杂关系，减少逻辑分散。例如：对比\"3. 自引用指针成员：表达层级或“父节点”\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "// 运行结果与先前手动 assignManager 的版本相同, 对比 https://godbolt.org/z/KdvfcM3hz\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Employee struct {\n",
    "\tName    string\n",
    "\tManager *Employee\n",
    "\tReports []*Employee\n",
    "}\n",
    "\n",
    "// NewEmployee 封装创建逻辑：如果传入 manager，自动把新员工挂到 manager 的 Reports 中。\n",
    "func NewEmployee(name string, manager *Employee) *Employee {\n",
    "\te := &Employee{Name: name, Manager: manager}\n",
    "\tif manager != nil {\n",
    "\t\tmanager.Reports = append(manager.Reports, e)\n",
    "\t}\n",
    "\treturn e\n",
    "}\n",
    "\n",
    "func printOrg(e *Employee, depth int) {\n",
    "\tif e == nil {\n",
    "\t\treturn\n",
    "\t}\n",
    "\tprefix := \"\"\n",
    "\tfor i := 0; i < depth; i++ {\n",
    "\t\tprefix += \"  \"\n",
    "\t}\n",
    "\tif e.Manager == nil {\n",
    "\t\tfmt.Printf(\"%s- %s (top manager)\\n\", prefix, e.Name)\n",
    "\t} else {\n",
    "\t\tfmt.Printf(\"%s- %s (manager: %s)\\n\", prefix, e.Name, e.Manager.Name)\n",
    "\t}\n",
    "\tfor _, report := range e.Reports {\n",
    "\t\tprintOrg(report, depth+1)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\t// 使用构造函数创建组织结构\n",
    "\talice := NewEmployee(\"Alice\", nil)  // 顶级经理\n",
    "\tbob := NewEmployee(\"Bob\", alice)    // Bob 向 Alice 汇报\n",
    "\tcarla := NewEmployee(\"Carla\", alice)\n",
    "\tdan := NewEmployee(\"Dan\", bob)\n",
    "\temma := NewEmployee(\"Emma\", bob)\n",
    "\n",
    "\tprintOrg(alice, 0)\n",
    "\n",
    "\tfmt.Println()\n",
    "\tfmt.Printf(\"%s -> Manager: %v, Reports: %d\\n\", bob.Name, bob.Manager.Name, len(bob.Reports))\n",
    "\tfmt.Printf(\"%s -> Manager: %v, Reports: %d\\n\", dan.Name, dan.Manager.Name, len(dan.Reports))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "  - 单一入口：所有创建逻辑集中在 NewEmployee，避免遗漏设置。\n",
    "  - 易于维护：若未来需要在创建时做更多初始化（例如分配 ID、记录日志），只需修改构造函数。\n",
    "  - 保证一致性：外部代码无法把 Reports 与 Manager 弄“不同步”，降低逻辑错误风险。\n",
    "\n",
    "这里的“保证一致性”指的是：**每当你创建或调整员工之间的上下级关系时，`Manager` 字段与 `Reports` 切片会保持同步，不会出现一边更新、另一边忘记更新的情况。**\n",
    "\n",
    "**为什么会不一致？**\n",
    "\n",
    "假设不用 `NewEmployee`，而是在外部这样操作：\n",
    "\n",
    "```go\n",
    "alice := &Employee{Name: \"Alice\"}\n",
    "bob := &Employee{Name: \"Bob\"}\n",
    "\n",
    "// 只设置了 Bob 的 Manager，但忘记把 Bob 放进 Alice.Reports\n",
    "bob.Manager = alice\n",
    "```\n",
    "\n",
    "此时：\n",
    "\n",
    "- `bob.Manager == alice`\n",
    "- 但 `alice.Reports` 还是空的。\n",
    "\n",
    "这会导致程序里有的逻辑认为 Bob 是 Alice 的下属（看 `Manager` 字段），另一些逻辑却认为 Alice 没有下属（看 `Reports` 切片），产生矛盾。\n",
    "\n",
    "**构造函数如何避免？**\n",
    "\n",
    "`NewEmployee` 把 **所有必需的更新都封装在一起**：\n",
    "\n",
    "\n",
    "```go\n",
    "func NewEmployee(name string, manager *Employee) *Employee {\n",
    "\te := &Employee{Name: name, Manager: manager}\n",
    "\tif manager != nil {\n",
    "\t\tmanager.Reports = append(manager.Reports, e) // 与 Manager 同步更新\n",
    "\t}\n",
    "\treturn e\n",
    "}\n",
    "```\n",
    "\n",
    "调用者只需：\n",
    "\n",
    "```go\n",
    "bob := NewEmployee(\"Bob\", alice)\n",
    "```\n",
    "\n",
    "就能确保：\n",
    "\n",
    "- `bob.Manager` 指向 `alice`\n",
    "- `alice.Reports` 中自动包含 `bob`\n",
    "\n",
    "调用者不可能忘记更新其中一项，从而维持了数据的一致性，降低了逻辑错误的风险。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "4. **JSON/序列化**：循环引用会导致 JSON 序列化时无限递归，应避免或自定义序列化逻辑（例如仅输出 ID 或剪断环）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 22 go语言左侧大括号为什么不能另起一行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p := Person{\n",
    "\tName: \"Alice\",\n",
    "\tAge:  30,\n",
    "\tAddress: Address{\n",
    "\t\tCity:   \"Shanghai\",\n",
    "\t\tStreet: \"Nanjing Rd\",\n",
    "\t}, // 注意结构体赋值最后一个加逗号\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 23 变量作用域"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 中，变量的**作用域（scope）**指的是该变量在源码中可被访问、使用的范围。主要分为以下几类：\n",
    "\n",
    "1. **包级作用域（Package Scope）**\n",
    "\n",
    "   - 在函数之外声明的变量、常量、类型、函数等，属于整个包可见。\n",
    "   - 同一个包内的所有文件都可以直接访问（如果是以大写开头，还能被其他包访问）。\n",
    "   \n",
    "\n",
    "   ```go\n",
    "   var pkgVar = \"package scope\" // 包内所有文件可见\n",
    "   ```\n",
    "\n",
    "2. **函数作用域（Function Scope）**\n",
    "\n",
    "   - 在函数体内部声明的变量，只能在该函数内使用。函数执行完毕后变量生命周期结束。\n",
    "\n",
    "\n",
    "   ```go\n",
    "   func f() {\n",
    "       var x = 10 // 仅在 f 内可见\n",
    "   }\n",
    "   ```\n",
    "\n",
    "3. **块作用域（Block Scope）**\n",
    "\n",
    "   - 在更小的代码块中声明的变量，只能在该块内可见：例如 `{ ... }`、`if`、`for`、`switch`、`case`、`range` 等语句块。\n",
    "   - 使用短变量声明 `:=` 也受限于所在的块。\n",
    "   \n",
    "\n",
    "   ```go\n",
    "   func g() {\n",
    "       if true {\n",
    "           y := 20 // 仅在 if 块内可见\n",
    "           fmt.Println(y)\n",
    "       }\n",
    "       // fmt.Println(y) // 编译错误：y 未定义\n",
    "   }\n",
    "   ```\n",
    "\n",
    "4. **循环变量的特殊性**\n",
    "\n",
    "   - `for range` 循环中的迭代变量在每次循环迭代时复用同一变量地址，常见于闭包中捕获变量的问题。处理方式通常是复制一份局部变量。\n",
    "\n",
    "   ```go\n",
    "   for _, v := range []int{1, 2, 3} {\n",
    "       vCopy := v // 若需要在闭包中使用，先复制\n",
    "       go func() {\n",
    "           fmt.Println(vCopy)\n",
    "       }()\n",
    "   }\n",
    "   ```\n",
    "\n",
    "5. **作用域 vs. 生命周期**\n",
    "\n",
    "   - 作用域是“能否访问到变量”的静态概念；\n",
    "   - 生命周期是“变量在运行时何时创建/销毁”的动态概念。\n",
    "   - 例如闭包可延长局部变量的生命周期，但作用域仍受语法限制。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 24 if语句及其局部变量"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基本 if / else 结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "及格了\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tscore := 85\n",
    "\n",
    "\tif score >= 60 {\n",
    "\t\tfmt.Println(\"及格了\")\n",
    "\t} else {\n",
    "\t\tfmt.Println(\"未及格\")\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 带初始化语句的 if"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请求成功: OK\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func fetchStatus() (string, error) {\n",
    "\treturn \"OK\", nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tif status, err := fetchStatus(); err != nil {\n",
    "\t\tfmt.Println(\"请求失败:\", err)\n",
    "\t} else {\n",
    "\t\tfmt.Println(\"请求成功:\", status)\n",
    "\t}\n",
    "\n",
    "\t// 这里访问 status 或 err 会编译错误，因为作用域仅限 if/else 内部\n",
    "\t// fmt.Println(status, err)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## if 块内局部变量作用域"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "value 在 if 内部可见：42\n",
      "if 块外无法访问 value\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tif value := 42; value > 40 {\n",
    "\t\tfmt.Printf(\"value 在 if 内部可见：%d\\n\", value)\n",
    "\t}\n",
    "\n",
    "\t// 下方如果取消注释，会提示 value 未定义\n",
    "\t// fmt.Println(value)\n",
    "\tfmt.Println(\"if 块外无法访问 value\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 变量遮蔽（shadowing）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "外部 result = 10\n",
      "if 内部 result = 100（遮蔽了外部变量）\n",
      "if 结束后外部 result 仍然是 10\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func compute() int {\n",
    "\treturn 100\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tresult := 10\n",
    "\tfmt.Printf(\"外部 result = %d\\n\", result)\n",
    "\n",
    "\tif result := compute(); result > 50 {\n",
    "\t\tfmt.Printf(\"if 内部 result = %d（遮蔽了外部变量）\\n\", result)\n",
    "\t}\n",
    "\n",
    "\tfmt.Printf(\"if 结束后外部 result 仍然是 %d\\n\", result)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 多分支共享初始化变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3 的两倍是 6，小于 10\n",
      "5 的两倍是 10，刚好等于 10\n",
      "7 的两倍是 14，大于 10\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func classify(n int) {\n",
    "\tif doubled := n * 2; doubled > 10 {\n",
    "\t\tfmt.Printf(\"%d 的两倍是 %d，大于 10\\n\", n, doubled)\n",
    "\t} else if doubled == 10 {\n",
    "\t\tfmt.Printf(\"%d 的两倍是 %d，刚好等于 10\\n\", n, doubled)\n",
    "\t} else {\n",
    "\t\tfmt.Printf(\"%d 的两倍是 %d，小于 10\\n\", n, doubled)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tclassify(3)\n",
    "\tclassify(5)\n",
    "\tclassify(7)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 25 for循环的各种变体"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 经典三段式 for（init; condition; post）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "i = 0\n",
      "i = 1\n",
      "i = 2\n",
      "i = 3\n",
      "i = 4\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tfor i := 0; i < 5; i++ {\n",
    "\t\tfmt.Printf(\"i = %d\\n\", i)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## for 的 while 变体（仅保留条件）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 + 2 + 3 + 4 + 5 = 15\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tsum := 0\n",
    "\tn := 1\n",
    "\n",
    "\tfor n <= 5 {\n",
    "\t\tsum += n\n",
    "\t\tn++\n",
    "\t}\n",
    "\n",
    "\tfmt.Printf(\"1 + 2 + 3 + 4 + 5 = %d\\n\", sum)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 无限循环 + break / continue"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "奇数：1\n",
      "奇数：3\n",
      "奇数：5\n",
      "奇数：7\n",
      "奇数：9\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tnum := 0\n",
    "\n",
    "\tfor {\n",
    "\t\tnum++\n",
    "\t\tif num%2 == 0 {\n",
    "\t\t\tcontinue // 跳过偶数\n",
    "\t\t}\n",
    "\t\tfmt.Printf(\"奇数：%d\\n\", num)\n",
    "\t\tif num >= 9 {\n",
    "\t\t\tbreak // 终止循环\n",
    "\t\t}\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## range 遍历切片（获取索引和值）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "names[0] = Alice\n",
      "names[1] = Bob\n",
      "names[2] = Charlie\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tnames := []string{\"Alice\", \"Bob\", \"Charlie\"}\n",
    "\n",
    "\tfor index, name := range names {\n",
    "\t\tfmt.Printf(\"names[%d] = %s\\n\", index, name)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## range 遍历 map（键值对）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Alice 的年龄是 25\n",
      "Bob 的年龄是 30\n",
      "Charlie 的年龄是 28\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tages := map[string]int{\n",
    "\t\t\"Alice\":   25,\n",
    "\t\t\"Bob\":     30,\n",
    "\t\t\"Charlie\": 28,\n",
    "\t}\n",
    "\n",
    "\tfor name, age := range ages {\n",
    "\t\tfmt.Printf(\"%s 的年龄是 %d\\n\", name, age)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## range 遍历字符串（按 rune 解码）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "索引 0 -> 字符 G (rune: U+0047)\n",
      "索引 1 -> 字符 o (rune: U+006F)\n",
      "索引 2 -> 字符 语 (rune: U+8BED)\n",
      "索引 5 -> 字符 言 (rune: U+8A00)\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\ttext := \"Go语言\"\n",
    "\n",
    "\tfor index, r := range text {\n",
    "\t\tfmt.Printf(\"索引 %d -> 字符 %c (rune: %U)\\n\", index, r, r)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## range 遍历通道（channel）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello\n",
      "from\n",
      "channel\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tch := make(chan string)\n",
    "\n",
    "\tgo func() {\n",
    "\t\tdefer close(ch)\n",
    "\t\tch <- \"Hello\"\n",
    "\t\tch <- \"from\"\n",
    "\t\tch <- \"channel\"\n",
    "\t}()\n",
    "\n",
    "\tfor msg := range ch {\n",
    "\t\tfmt.Println(msg)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 26 label与goto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`label`（标签）在 Go 中主要与 `goto`、`break`、`continue` 搭配使用。`goto` 可以带来灵活的流程控制，但在复杂场景中仍应优先考虑更清晰的结构化控制逻辑。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用 goto 配合标签重试逻辑"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第 1 次尝试失败：模拟失败\n",
      "第 2 次尝试失败：模拟失败\n",
      "第 3 次尝试成功，任务完成！\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "func doTask(attempt int) error {\n",
    "\tif attempt < 3 {\n",
    "\t\treturn errors.New(\"模拟失败\")\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tattempt := 0\n",
    "\n",
    "retry:\n",
    "\tattempt++\n",
    "\terr := doTask(attempt)\n",
    "\tif err != nil {\n",
    "\t\tfmt.Printf(\"第 %d 次尝试失败：%v\\n\", attempt, err)\n",
    "\t\tgoto retry // 跳转到 retry 标签，重新尝试\n",
    "\t}\n",
    "\n",
    "\tfmt.Printf(\"第 %d 次尝试成功，任务完成！\\n\", attempt)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 在多重循环中结合 goto 快速跳出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "检查 i=1, j=1\n",
      "检查 i=1, j=2\n",
      "检查 i=1, j=3\n",
      "检查 i=2, j=1\n",
      "检查 i=2, j=2\n",
      "找到乘积为 4 的组合\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "    found := false\n",
    "\n",
    "    for i := 1; i <= 3; i++ {\n",
    "        for j := 1; j <= 3; j++ {\n",
    "            fmt.Printf(\"检查 i=%d, j=%d\\n\", i, j)\n",
    "            if i*j == 4 {\n",
    "                found = true\n",
    "                goto done // 找到目标，跳出所有循环\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "\n",
    "done:\n",
    "    if found {\n",
    "        fmt.Println(\"找到乘积为 4 的组合\")\n",
    "    } else {\n",
    "        fmt.Println(\"未找到满足条件的组合\")\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 27 switch表达式与fallthrough"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 普通表达式 switch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 适合做范围判断，结构更清晰，比多重 `if-else` 更易读"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tscore := 83\n",
    "\n",
    "\tswitch {\n",
    "\tcase score >= 90:\n",
    "\t\tfmt.Println(\"A\")\n",
    "\tcase score >= 80:\n",
    "\t\tfmt.Println(\"B\")\n",
    "\tcase score >= 70:\n",
    "\t\tfmt.Println(\"C\")\n",
    "\tcase score >= 60:\n",
    "\t\tfmt.Println(\"D\")\n",
    "\tdefault:\n",
    "\t\tfmt.Println(\"F\")\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 直接对值进行匹配的 switch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 每个 `case` 默认自带 `break`（除非使用 `fallthrough`），因此匹配后会自动跳出 `switch`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tweekday := 3\n",
    "\n",
    "\tswitch weekday {\n",
    "\tcase 1:\n",
    "\t\tfmt.Println(\"Monday\")\n",
    "\tcase 2:\n",
    "\t\tfmt.Println(\"Tuesday\")\n",
    "\tcase 3:\n",
    "\t\tfmt.Println(\"Wednesday\")\n",
    "\tcase 4:\n",
    "\t\tfmt.Println(\"Thursday\")\n",
    "\tcase 5:\n",
    "\t\tfmt.Println(\"Friday\")\n",
    "\tdefault:\n",
    "\t\tfmt.Println(\"Weekend\")\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## fallthrough 的经典示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `fallthrough` 只能出现在 `case` 子句的最后一行，它会强制执行紧邻下一个 `case` 的代码块，而 **不会** 再做条件匹配。\n",
    "- 典型用途是当多个分支逻辑相近，希望一次执行多个 `case` 的代码时。但需要谨慎使用，否则容易造成逻辑混乱。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Matched 2\n",
      "Reached 3 (due to fallthrough)\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tswitch num := 2; num {\n",
    "\tcase 1:\n",
    "\t\tfmt.Println(\"Matched 1\")\n",
    "\tcase 2:\n",
    "\t\tfmt.Println(\"Matched 2\")\n",
    "\t\tfallthrough\n",
    "\tcase 3:\n",
    "\t\tfmt.Println(\"Reached 3 (due to fallthrough)\")\n",
    "\tdefault:\n",
    "\t\tfmt.Println(\"Default\")\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类型断言 switch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 这是“类型 switch”，用于根据动态类型执行不同逻辑。\n",
    "- `v := i.(type)` 只能出现在 `switch` 语句里，编译器会推断 `v` 在每个 `case` 中的具体类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func describe(i interface{}) {\n",
    "\tswitch v := i.(type) {\n",
    "\tcase int:\n",
    "\t\tfmt.Printf(\"int: %d\\n\", v)\n",
    "\tcase string:\n",
    "\t\tfmt.Printf(\"string: %q\\n\", v)\n",
    "\tcase bool:\n",
    "\t\tfmt.Printf(\"bool: %t\\n\", v)\n",
    "\tdefault:\n",
    "\t\tfmt.Printf(\"unknown type %T\\n\", v)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tdescribe(42)\n",
    "\tdescribe(\"go\")\n",
    "\tdescribe(true)\n",
    "\tdescribe(3.14)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 28 位运算及其应用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 运算符 | 名称           | 示例 (二进制)         | 说明                              |\n",
    "| ------ | -------------- | --------------------- | --------------------------------- |\n",
    "| `&`    | 按位与         | `1010 & 1100 = 1000`  | 两位都为 1 时结果为 1             |\n",
    "| `|`    | 按位或         | `1010 | 1100 = 1110`  | 有任意一位为 1 时结果为 1         |\n",
    "| `^`    | 按位异或       | `1010 ^ 1100 = 0110`  | 不同为 1，相同为 0                |\n",
    "| `&^`   | 按位与非（Go） | `1010 &^ 1100 = 0010` | 保留左操作数中，右操作数为 0 的位 |\n",
    "| `~`    | 按位取反       | `~1010 = 0101`        | 将 0 变 1，1 变 0                 |\n",
    "| `<<`   | 左移           | `0010 << 2 = 1000`    | 等价于乘以 `2^n`                  |\n",
    "| `>>`   | 右移           | `1000 >> 2 = 0010`    | 等价于除以 `2^n`（取整）          |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **标志位/状态管理**：操作系统内核、驱动、图形硬件寄存器。\n",
    "- **网络与通信协议**：IPv4/IPv6 头部、TCP 标志位、压缩算法标志。\n",
    "- **加密与校验**：CRC、哈希函数中的混淆操作。\n",
    "- **图形音视频**：像素格式转换、音频采样处理。\n",
    "- **算法优化**：位图（bitset）、快速集合判重、棋类程序的棋盘状态压缩。\n",
    "- **数据库/存储**：布隆过滤器、压缩索引、权限字段。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "101\n"
     ]
    }
   ],
   "source": [
    "%%\n",
    "fmt.Printf(\"%b\\n\", 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a & b  = 10001000\n",
      "a | b  = 11101110\n",
      "a ^ b  = 01100110\n",
      "^a     = 01010101\n",
      "a &^ b = 00100010\n",
      "a << 2 = 10101000\n",
      "a >> 3 = 00010101\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tvar a uint8 = 0b10101010\n",
    "\tvar b uint8 = 0b11001100\n",
    "\n",
    "\tfmt.Printf(\"a & b  = %08b\\n\", a&b)   // 10001000\n",
    "\tfmt.Printf(\"a | b  = %08b\\n\", a|b)   // 11101110\n",
    "\tfmt.Printf(\"a ^ b  = %08b\\n\", a^b)   // 01100110\n",
    "\tfmt.Printf(\"^a     = %08b\\n\", ^a)    // 01010101\n",
    "\tfmt.Printf(\"a &^ b = %08b\\n\", a&^b)  // 00100010\n",
    "\n",
    "\tfmt.Printf(\"a << 2 = %08b\\n\", a<<2)  // 10100000\n",
    "\tfmt.Printf(\"a >> 3 = %08b\\n\", a>>3)  // 00010101\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用位掩码管理布尔标志"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Has write permission\n",
      "Final bitmask: 110\n"
     ]
    }
   ],
   "source": [
    "// 应用领域：操作系统权限、功能开关、游戏状态管理等。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "const (\n",
    "\tReadPermission  = 1 << iota // 1 << 0 -> 0001\n",
    "\tWritePermission             // 1 << 1 -> 0010\n",
    "\tExecPermission              // 1 << 2 -> 0100\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\t// 用户权限：读+写\n",
    "\tpermissions := ReadPermission | WritePermission\n",
    "\n",
    "\t// 检查是否有写权限\n",
    "\tif permissions&WritePermission != 0 {\n",
    "\t\tfmt.Println(\"Has write permission\")\n",
    "\t}\n",
    "\n",
    "\t// 添加执行权限\n",
    "\tpermissions |= ExecPermission\n",
    "\n",
    "\t// 移除读权限\n",
    "\tpermissions &^= ReadPermission\n",
    "\n",
    "\tfmt.Printf(\"Final bitmask: %03b\\n\", permissions)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 快速判断数字特性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true\n",
      "true\n",
      "false\n"
     ]
    }
   ],
   "source": [
    "// 应用领域：性能敏感的数学运算、算法优化。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func isEven(n int) bool {\n",
    "\treturn n&1 == 0\n",
    "}\n",
    "\n",
    "func isPowerOfTwo(n int) bool {\n",
    "\treturn n > 0 && (n&(n-1)) == 0\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(isEven(42))        // true\n",
    "\tfmt.Println(isPowerOfTwo(64))  // true\n",
    "\tfmt.Println(isPowerOfTwo(70))  // false\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 打包 / 解包多字段（Bit Fields）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Packed: 0xA37C\n",
      "Unpacked: A 3 7 C\n"
     ]
    }
   ],
   "source": [
    "// 应用领域：网络协议（如 IP 头部字段）、嵌入式存储优化、图形渲染数据压缩。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// 打包 4 个 4-bit 数字到一个 16-bit 容器里\n",
    "func pack(a, b, c, d uint16) uint16 {\n",
    "\treturn (a << 12) | (b << 8) | (c << 4) | d\n",
    "}\n",
    "\n",
    "func unpack(value uint16) (a, b, c, d uint16) {\n",
    "\ta = (value >> 12) & 0xF\n",
    "\tb = (value >> 8) & 0xF\n",
    "\tc = (value >> 4) & 0xF\n",
    "\td = value & 0xF\n",
    "\treturn\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvalue := pack(0xA, 0x3, 0x7, 0xC)\n",
    "\tfmt.Printf(\"Packed: 0x%04X\\n\", value)\n",
    "\n",
    "\ta, b, c, d := unpack(value)\n",
    "\tfmt.Printf(\"Unpacked: %X %X %X %X\\n\", a, b, c, d)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 利用位运算实现集合（Bitset）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true\n",
      "false\n",
      "false\n"
     ]
    }
   ],
   "source": [
    "// 应用领域：布尔集合、图算法（如判重、状态搜索）、布隆过滤器等。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type BitSet uint32\n",
    "\n",
    "func (s *BitSet) Add(x uint) {\n",
    "\t*s |= 1 << x\n",
    "}\n",
    "\n",
    "func (s BitSet) Contains(x uint) bool {\n",
    "\treturn s&(1<<x) != 0\n",
    "}\n",
    "\n",
    "func (s *BitSet) Remove(x uint) {\n",
    "\t*s &^= 1 << x\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvar set BitSet\n",
    "\tset.Add(1)\n",
    "\tset.Add(3)\n",
    "\tset.Add(5)\n",
    "\n",
    "\tfmt.Println(set.Contains(3)) // true\n",
    "\tfmt.Println(set.Contains(2)) // false\n",
    "\n",
    "\tset.Remove(3)\n",
    "\tfmt.Println(set.Contains(3)) // false\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 快速交换变量（异或交换）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9 5\n"
     ]
    }
   ],
   "source": [
    "// 虽然现代编译器通常会优化临时变量的交换，这个技巧主要用于理解 XOR 的性质。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\ta, b := 5, 9\n",
    "\ta ^= b\n",
    "\tb ^= a\n",
    "\ta ^= b\n",
    "\tfmt.Println(a, b) // 9 5\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 计算二进制中 1 的个数（汉明重量）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n",
      "6\n"
     ]
    }
   ],
   "source": [
    "// 应用领域：校验码、压缩算法、密码学、图像处理。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"math/bits\"\n",
    ")\n",
    "\n",
    "func manualPopCount(x uint) int {\n",
    "\tcount := 0\n",
    "\tfor x != 0 {\n",
    "\t\tx &= x - 1 // 每次消掉最低位的 1\n",
    "\t\tcount++\n",
    "\t}\n",
    "\treturn count\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvar value uint = 0b1101010101\n",
    "\tfmt.Println(manualPopCount(value)) // 6\n",
    "\tfmt.Println(bits.OnesCount(value)) // 标准库函数\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 位运算实现乘除以 2 的幂"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "56\n",
      "7\n"
     ]
    }
   ],
   "source": [
    "// 应用领域：图形学计算、音视频处理、嵌入式系统优化。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func mulByPowerOfTwo(n, k int) int {\n",
    "\treturn n << k // 乘以 2^k\n",
    "}\n",
    "\n",
    "func divByPowerOfTwo(n, k int) int {\n",
    "\treturn n >> k // 整除 2^k\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(mulByPowerOfTwo(7, 3)) // 56\n",
    "\tfmt.Println(divByPowerOfTwo(56, 3)) // 7\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 29 函数&go语言的拷贝原则"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 基本函数声明与调用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3 + 5 = 8\n"
     ]
    }
   ],
   "source": [
    "// 使用 func 声明、传入参数并返回结果，体现 Go 函数的基本语法\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func add(x int, y int) int {\n",
    "\treturn x + y\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tsum := add(3, 5)\n",
    "\tfmt.Println(\"3 + 5 =\", sum)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 多返回值与命名返回值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10 / 2 = 5 error: <nil>\n",
      "10 / 0 = 0 error: divide by zero\n"
     ]
    }
   ],
   "source": [
    "// Go 函数可以返回多个值，常用于“结果 + 错误”。命名返回值可在函数体内直接赋值后 return\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "func divide(a, b float64) (quotient float64, err error) {\n",
    "\tif b == 0 {\n",
    "\t\treturn 0, errors.New(\"divide by zero\")\n",
    "\t}\n",
    "\tquotient = a / b\n",
    "\treturn\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tq, err := divide(10, 2)\n",
    "\tfmt.Println(\"10 / 2 =\", q, \"error:\", err)\n",
    "\n",
    "\tq, err = divide(10, 0)\n",
    "\tfmt.Println(\"10 / 0 =\", q, \"error:\", err)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 变长参数（Variadic）函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sum() = 0\n",
      "sum(1,2,3) = 6\n",
      "sum(5,10,15,20) = 50\n"
     ]
    }
   ],
   "source": [
    "// 使用 ...T 声明变长参数，在函数内部视为切片，可接受任意数量的实参\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func sum(nums ...int) int {\n",
    "\ttotal := 0\n",
    "\tfor _, v := range nums {\n",
    "\t\ttotal += v\n",
    "\t}\n",
    "\treturn total\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"sum() =\", sum())\n",
    "\tfmt.Println(\"sum(1,2,3) =\", sum(1, 2, 3))\n",
    "\tfmt.Println(\"sum(5,10,15,20) =\", sum(5, 10, 15, 20))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 匿名函数与闭包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "// 匿名函数可以捕获外部变量形成闭包，使得函数调用间共享状态\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tcounter := 0\n",
    "\tnext := func() int {\n",
    "\t\tcounter++\n",
    "\t\treturn counter\n",
    "\t}\n",
    "\n",
    "\tfmt.Println(next()) // 1\n",
    "\tfmt.Println(next()) // 2\n",
    "\tfmt.Println(next()) // 3\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 方法（Method）与接收者"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Point after Move: &{X:4 Y:6}\n"
     ]
    }
   ],
   "source": [
    "// 给类型定义方法，实现面向对象风格的行为封装。接收者可为值或指针\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Point struct {\n",
    "\tX, Y int\n",
    "}\n",
    "\n",
    "func (p *Point) Move(dx, dy int) {\n",
    "\tp.X += dx\n",
    "\tp.Y += dy\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tpt := &Point{X: 1, Y: 2}\n",
    "\tpt.Move(3, 4)\n",
    "\tfmt.Printf(\"Point after Move: %+v\\n\", pt)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 递归函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5! = 120\n",
      "10! = 3628800\n"
     ]
    }
   ],
   "source": [
    "// 函数可调用自身，实现诸如阶乘、树遍历等递归算法\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func factorial(n int) int {\n",
    "\tif n <= 1 {\n",
    "\t\treturn 1\n",
    "\t}\n",
    "\treturn n * factorial(n-1)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"5! =\", factorial(5))\n",
    "\tfmt.Println(\"10! =\", factorial(10))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用 defer 处理收尾逻辑"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "开始工作\n",
      "工作进行中……\n",
      "清理资源（defer）\n"
     ]
    }
   ],
   "source": [
    "// defer 语句在函数返回前执行，常用于资源释放或统一收尾操作\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func doWork() {\n",
    "\tfmt.Println(\"开始工作\")\n",
    "\tdefer fmt.Println(\"清理资源（defer）\")\n",
    "\tfmt.Println(\"工作进行中……\")\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tdoWork()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 函数类型与回调"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "apply(add, 3, 4) = 7\n",
      "apply(mul, 3, 4) = 12\n"
     ]
    }
   ],
   "source": [
    "// 函数是“一等公民”，可以赋值给变量、作为参数传入，实现策略或回调模式\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Operation func(int, int) int\n",
    "\n",
    "func apply(op Operation, a, b int) int {\n",
    "\treturn op(a, b)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tadd := func(x, y int) int { return x + y }\n",
    "\tmul := func(x, y int) int { return x * y }\n",
    "\n",
    "\tfmt.Println(\"apply(add, 3, 4) =\", apply(add, 3, 4))\n",
    "\tfmt.Println(\"apply(mul, 3, 4) =\", apply(mul, 3, 4))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## go语言的拷贝原则"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **判断是否需要复制**：如果函数只读，值传递没问题；若需修改原数据或数据过大，考虑传指针或切片。\n",
    "- **明晰“浅拷贝 vs. 深拷贝”**：Go 默认是浅拷贝，需要深拷贝时请手写复制逻辑或使用专门库。\n",
    "- **避免不必要的逃逸**：尽量在函数中使用值类型或栈变量，减少堆分配。\n",
    "- **写代码时先理解数据布局**：掌握切片头、map 头、接口表等底层结构，有助于判断修改是否影响原数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 常见误区总结\n",
    "\n",
    "| 场景                         | 误解                           | 正确认识                                                     |\n",
    "| ---------------------------- | ------------------------------ | ------------------------------------------------------------ |\n",
    "| “Go 支持引用传递”            | Go 会按引用方式传参            | Go **只有值传递**；所谓引用效果是一份「指针值」的拷贝带来的  |\n",
    "| 修改切片长度失败             | 在函数中 `append` 后主切片不变 | `append` 返回的切片描述可能指向新数组，需把返回值赋回        |\n",
    "| map 参数是深度拷贝           | 修改 map 里的值不会影响外部    | map 传参复制的是指向底层哈希表的指针，修改会影响原 map       |\n",
    "| 接口传值不影响原对象         | 认为接口值是复制体             | 接口内部仍持有原对象的指针（若动态值为指针类型），方法调用会修改原对象 |\n",
    "| 多次返回大结构体导致性能问题 | 都会复制大块内存               | 编译器优化可消除多余拷贝，但热点路径仍建议使用指针或分片化结构体 |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 基础类型：完整值被拷贝"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "函数内 x: 11\n",
      "函数外 a: 10\n"
     ]
    }
   ],
   "source": [
    "// int、bool、float64 等纯值类型，传参时复制整个值。\n",
    "// 函数内部的修改不会影响调用者的变量。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func increment(x int) {\n",
    "\tx++\n",
    "\tfmt.Println(\"函数内 x:\", x)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\ta := 10\n",
    "\tincrement(a)\n",
    "\tfmt.Println(\"函数外 a:\", a)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 结构体与数组：整体复制"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "movePoint 内: {11 21}\n",
      "movePoint 后: {10 20}\n",
      "changeArray 内: [99 2 3]\n",
      "changeArray 后: [1 2 3]\n"
     ]
    }
   ],
   "source": [
    "// struct 和 array 在函数调用时也会被完整复制。\n",
    "// 对于较大的结构体和数组，频繁复制可能带来性能开销。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Point struct {\n",
    "\tX, Y int\n",
    "}\n",
    "\n",
    "func movePoint(p Point) {\n",
    "\tp.X++\n",
    "\tp.Y++\n",
    "\tfmt.Println(\"movePoint 内:\", p)\n",
    "}\n",
    "\n",
    "func changeArray(arr [3]int) {\n",
    "\tarr[0] = 99\n",
    "\tfmt.Println(\"changeArray 内:\", arr)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tpt := Point{X: 10, Y: 20}\n",
    "\tmovePoint(pt)\n",
    "\tfmt.Println(\"movePoint 后:\", pt)\n",
    "\n",
    "\ta := [3]int{1, 2, 3}\n",
    "\tchangeArray(a)\n",
    "\tfmt.Println(\"changeArray 后:\", a)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 切片、映射、通道：拷贝的是「描述符」"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "mutateSlice 内: [0 42 0]\n",
      "mutateSlice 后: [0 42 0]\n",
      "appendSlice 内: [0 42 0 99] len: 4 cap: 6\n",
      "appendSlice 后: [0 42 0]\n",
      "mutateMap 内: map[Go:2]\n",
      "mutateMap 后: map[Go:2]\n"
     ]
    }
   ],
   "source": [
    "// slice、map、chan、func、interface 都是“小对象”，内部存的是指向底层数据的指针 + 元信息。\n",
    "// 传参时复制的是这个“描述符”，所以指向的底层数据通常仍然共享。\n",
    "// 对共享数据的修改会影响原值；但如果在函数内让描述符指向别的底层数据（如切片扩容、make 新 map），这种改变只作用在副本上。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func mutateSlice(s []int) {\n",
    "\ts[1] = 42\n",
    "\tfmt.Println(\"mutateSlice 内:\", s)\n",
    "}\n",
    "\n",
    "func appendSlice(s []int) {\n",
    "\ts = append(s, 99) // 可能重新分配底层数组\n",
    "\tfmt.Println(\"appendSlice 内:\", s, \"len:\", len(s), \"cap:\", cap(s))\n",
    "}\n",
    "\n",
    "func mutateMap(m map[string]int) {\n",
    "\tm[\"Go\"] = 2\n",
    "\tfmt.Println(\"mutateMap 内:\", m)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\ts := []int{0, 0, 0}\n",
    "\tmutateSlice(s)\n",
    "\tfmt.Println(\"mutateSlice 后:\", s)\n",
    "\n",
    "\tappendSlice(s)\n",
    "\tfmt.Println(\"appendSlice 后:\", s)\n",
    "\n",
    "\tm := map[string]int{\"Go\": 1}\n",
    "\tmutateMap(m)\n",
    "\tfmt.Println(\"mutateMap 后:\", m)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 字符串：只读的切片头"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原字符串: hello\n",
      "拷贝字符串: hello\n",
      "修改后的 []byte: Hello\n",
      "原字符串仍是: hello\n"
     ]
    }
   ],
   "source": [
    "// 字符串的拷贝类似切片头，但底层字节数组不可修改。\n",
    "// 多个字符串共享底层数据是安全的。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\ts := \"hello\"\n",
    "\tcopy := s\n",
    "\n",
    "\tfmt.Println(\"原字符串:\", s)\n",
    "\tfmt.Println(\"拷贝字符串:\", copy)\n",
    "\n",
    "\tb := []byte(s)\n",
    "\tb[0] = 'H'\n",
    "\tfmt.Println(\"修改后的 []byte:\", string(b))\n",
    "\tfmt.Println(\"原字符串仍是:\", s)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 指针：复制地址，实现“引用效果”"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Counter.N: 2\n"
     ]
    }
   ],
   "source": [
    "// 传入指针时，函数获得的是指针值的拷贝（地址）。\n",
    "// 通过指针访问的底层数据可被修改，体现“引用传递”效果。\n",
    "// 指针本身在函数中若被重新赋值，不会改变调用者的指针。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Counter struct {\n",
    "\tN int\n",
    "}\n",
    "\n",
    "func addOne(c *Counter) {\n",
    "\tc.N++\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tcounter := &Counter{}\n",
    "\taddOne(counter)\n",
    "\taddOne(counter)\n",
    "\tfmt.Println(\"Counter.N:\", counter.N)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 接口：复制接口表头"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调用接口方法后: 1\n"
     ]
    }
   ],
   "source": [
    "// 接口值包含「动态类型指针 + 动态值指针」的结构体。\n",
    "// 传参复制的是接口头部，但仍指向原始动态值。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Increaser interface {\n",
    "\tInc()\n",
    "}\n",
    "\n",
    "type Number struct {\n",
    "\tValue int\n",
    "}\n",
    "\n",
    "func (n *Number) Inc() {\n",
    "\tn.Value++\n",
    "}\n",
    "\n",
    "func callInc(i Increaser) {\n",
    "\ti.Inc()\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tnum := &Number{}\n",
    "\tcallInc(num)\n",
    "\tfmt.Println(\"调用接口方法后:\", num.Value)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 函数返回值同样遵循拷贝原则"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "函数内新建: {1 2}\n",
      "函数外接收: {1 2}\n"
     ]
    }
   ],
   "source": [
    "// 返回值也是按值复制。\n",
    "// 编译器可利用逃逸分析将临时值分配在栈或堆上；这不影响“值语义”。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Point struct {\n",
    "\tX, Y int\n",
    "}\n",
    "\n",
    "func newPoint() Point {\n",
    "\tp := Point{1, 2}\n",
    "\tfmt.Println(\"函数内新建:\", p)\n",
    "\treturn p // 返回时复制一份给调用者\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tpt := newPoint()\n",
    "\tfmt.Println(\"函数外接收:\", pt)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 经典惯用法：指针或切片返回修改结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "// 避免结构体拷贝开销\n",
    "\n",
    "func movePoint(p *Point) {\n",
    "\tp.X++\n",
    "\tp.Y++\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "// 链式操作（返回切片或 map）\n",
    "\n",
    "func appendItem(s []int, v int) []int {\n",
    "\treturn append(s, v)\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Counter.N: 2\n",
      "切片: [1 2 3]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Counter struct {\n",
    "\tN int\n",
    "}\n",
    "\n",
    "func addAndReturn(c *Counter) *Counter {\n",
    "\tc.N++\n",
    "\treturn c // 返回同一个指针，调用方继续使用\n",
    "}\n",
    "\n",
    "func appendAndReturn(s []int, v int) []int {\n",
    "\ts = append(s, v)\n",
    "\treturn s // 返回新的切片描述符\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tcounter := &Counter{}\n",
    "\tcounter = addAndReturn(counter)\n",
    "\tcounter = addAndReturn(counter)\n",
    "\tfmt.Println(\"Counter.N:\", counter.N)\n",
    "\n",
    "\ts := []int{1, 2}\n",
    "\ts = appendAndReturn(s, 3)\n",
    "\tfmt.Println(\"切片:\", s)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 30 数组&函数参数需要传数组指针吗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数组"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **固定长度**：数组长度是类型的一部分，`[3]int` 与 `[4]int` 是不同类型。\n",
    "- **连续内存**：存储同一类型元素，零值初始化。\n",
    "- **值语义**：赋值、传参时会整体拷贝，需要共享数据可改用切片或指针。\n",
    "- **编译期已知长度**：长度必须在编译期确定，可通过常量或 `...` 推断。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始零值数组: [0 0 0]\n",
      "字面量初始化: [1 2 3]\n",
      "自动推断长度: [Go Rust Swift] 长度: 3\n",
      "b[0] = 1\n",
      "b[1] = 2\n",
      "b[2] = 3\n",
      "showCopy 内部: [999 2 3]\n",
      "showCopy 调用后 b 仍旧: [1 2 3]\n",
      "修改 b[1] 后: [1 42 3]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// showCopy 接受一个数组参数，对其修改；由于数组是值拷贝，函数外不会受影响。\n",
    "func showCopy(arr [3]int) {\n",
    "\tarr[0] = 999\n",
    "\tfmt.Println(\"showCopy 内部:\", arr)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\t// 1. 声明并零值初始化\n",
    "\tvar a [3]int\n",
    "\tfmt.Println(\"初始零值数组:\", a)\n",
    "\n",
    "\t// 2. 使用字面量初始化\n",
    "\tb := [3]int{1, 2, 3}\n",
    "\tfmt.Println(\"字面量初始化:\", b)\n",
    "\n",
    "\t// 3. 让编译器推断长度\n",
    "\tc := [...]string{\"Go\", \"Rust\", \"Swift\"}\n",
    "\tfmt.Println(\"自动推断长度:\", c, \"长度:\", len(c))\n",
    "\n",
    "\t// 4. 遍历数组\n",
    "\tfor i, v := range b {\n",
    "\t\tfmt.Printf(\"b[%d] = %d\\n\", i, v)\n",
    "\t}\n",
    "\n",
    "\t// 5. 验证值语义\n",
    "\tshowCopy(b)\n",
    "\tfmt.Println(\"showCopy 调用后 b 仍旧:\", b)\n",
    "\n",
    "\t// 6. 修改原数组\n",
    "\tb[1] = 42\n",
    "\tfmt.Println(\"修改 b[1] 后:\", b)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 修改二维数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始数组: [[1 2 3] [4 5 6]]\n",
      "addToMatrix 后: [[11 12 13] [14 15 16]]\n",
      "overwriteMatrix 后: [[7 8 9] [10 11 12]]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// addToMatrix 使用数组指针，批量为所有元素加上 delta。\n",
    "func addToMatrix(m *[2][3]int, delta int) {\n",
    "\tfor i := range m {\n",
    "\t\tfor j := range m[i] {\n",
    "\t\t\tm[i][j] += delta\n",
    "\t\t}\n",
    "\t}\n",
    "}\n",
    "\n",
    "// overwriteMatrix 接受切片，批量覆盖元素。\n",
    "// 假设传入的 newValues 维度与 m 一致。\n",
    "func overwriteMatrix(m [][]int, newValues [][]int) {\n",
    "\tfor i := range m {\n",
    "\t\tfor j := range m[i] {\n",
    "\t\t\tm[i][j] = newValues[i][j]\n",
    "\t\t}\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tmatrix := [2][3]int{\n",
    "\t\t{1, 2, 3},\n",
    "\t\t{4, 5, 6},\n",
    "\t}\n",
    "\tfmt.Println(\"原始数组:\", matrix)\n",
    "\n",
    "\t// 方法一：传数组指针，整体加 10\n",
    "\taddToMatrix(&matrix, 10)\n",
    "\tfmt.Println(\"addToMatrix 后:\", matrix)\n",
    "\n",
    "\t// 方法二：转换为切片，批量覆盖新值\n",
    "\tmatrixSlice := make([][]int, len(matrix))\n",
    "\tfor i := range matrix {\n",
    "\t\tmatrixSlice[i] = matrix[i][:] // 与底层数组共享数据\n",
    "\t}\n",
    "\n",
    "\tnewValues := [][]int{\n",
    "\t\t{7, 8, 9},\n",
    "\t\t{10, 11, 12},\n",
    "\t}\n",
    "\toverwriteMatrix(matrixSlice, newValues)\n",
    "\tfmt.Println(\"overwriteMatrix 后:\", matrix)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数参数需要传数组指针吗"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 中，数组是 **值类型**：函数参数按值传递会整体拷贝一份数组副本。\n",
    " 是否需要传数组指针，取决于你的需求：\n",
    "\n",
    "- **只读或临时使用**：直接传值即可，函数内部修改不会影响原数组。\n",
    "- **需要修改原数组内容**或 **避免拷贝成本**（尤其是大数组）时，才建议传指针（或改用切片）。\n",
    "\n",
    "换言之，“需不需要传指针”并无硬性规定，而是根据是否希望共享底层数据或节省内存来决定。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "modifyByValue 内部: [100 2 3]\n",
      "调用 modifyByValue 后 a: [1 2 3]\n",
      "modifyByPointer 内部: &[200 2 3]\n",
      "调用 modifyByPointer 后 a: [200 2 3]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// modifyByValue 接受数组副本；修改只作用于副本。\n",
    "func modifyByValue(arr [3]int) {\n",
    "\tarr[0] = 100\n",
    "\tfmt.Println(\"modifyByValue 内部:\", arr)\n",
    "}\n",
    "\n",
    "// modifyByPointer 接受数组指针；修改作用于原数组。\n",
    "func modifyByPointer(arr *[3]int) {\n",
    "\tarr[0] = 200\n",
    "\tfmt.Println(\"modifyByPointer 内部:\", arr)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\ta := [3]int{1, 2, 3}\n",
    "\n",
    "\tmodifyByValue(a)\n",
    "\tfmt.Println(\"调用 modifyByValue 后 a:\", a)\n",
    "\n",
    "\tmodifyByPointer(&a)\n",
    "\tfmt.Println(\"调用 modifyByPointer 后 a:\", a)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用切片替代数组指针: 切片本质上是一个包含指向底层数组的描述符（指针、长度、容量），复制切片时只是复制描述符，仍共享同一块底层数组。因此函数内部修改切片的元素，会直接影响原始数据。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原数组: [1 2 3]\n",
      "modifySlice 内部: [999 2 3]\n",
      "调用 modifySlice 后arr数组: [999 2 3]\n",
      "调用 modifySlice 后slice切片: [999 2 3]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// modifySlice 接受切片，修改元素会反映到底层数组\n",
    "func modifySlice(s []int) {\n",
    "\ts[0] = 999\n",
    "\tfmt.Println(\"modifySlice 内部:\", s)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tarr := [3]int{1, 2, 3}\n",
    "\tfmt.Println(\"原数组:\", arr)\n",
    "\n",
    "\t// 将数组转换为切片\n",
    "\tslice := arr[:]\n",
    "\n",
    "\tmodifySlice(slice)\n",
    "\tfmt.Println(\"调用 modifySlice 后arr数组:\", arr)\n",
    "\tfmt.Println(\"调用 modifySlice 后slice切片:\", slice)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 31 切片及扩容规律"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**切片是动态视图**：由指向底层数组的指针、长度（len）与容量（cap）组成。复制切片只是复制这三元组，依旧指向同一底层数据。\n",
    "\n",
    "**len 与 cap**：\n",
    "\n",
    "- `len` 表示当前元素个数。\n",
    "- `cap` 表示底层数组从切片起始位置开始最多可容纳的元素数。\n",
    "\n",
    "**自动扩容**：\n",
    "\n",
    "- 当 `append` 使长度超过容量时，会分配新的更大底层数组，将旧数据拷贝过去，再追加新元素。\n",
    "- 扩容倍数与具体实现有关：早期 Go 版本通常按 2 倍扩容；1.18 之后，容量较大时会趋向于 1.25 倍，保证内存使用与性能之间的平衡。\n",
    "\n",
    "**共享底层数组的注意事项**：多个切片共享底层数组时，一个切片的修改可能影响到另一个；扩容后，切片可能指向新的底层数组，从而与旧切片分离。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始         len= 0 cap= 0 data=[]\n",
      "append  1  len= 1 cap= 1 data=[1]\n",
      "append  2  len= 2 cap= 2 data=[1 2]\n",
      "append  3  len= 3 cap= 4 data=[1 2 3]\n",
      "append  4  len= 4 cap= 4 data=[1 2 3 4]\n",
      "append  5  len= 5 cap= 8 data=[1 2 3 4 5]\n",
      "append  6  len= 6 cap= 8 data=[1 2 3 4 5 6]\n",
      "append  7  len= 7 cap= 8 data=[1 2 3 4 5 6 7]\n",
      "append  8  len= 8 cap= 8 data=[1 2 3 4 5 6 7 8]\n",
      "append  9  len= 9 cap=16 data=[1 2 3 4 5 6 7 8 9]\n",
      "append 10  len=10 cap=16 data=[1 2 3 4 5 6 7 8 9 10]\n",
      "append 11  len=11 cap=16 data=[1 2 3 4 5 6 7 8 9 10 11]\n",
      "append 12  len=12 cap=16 data=[1 2 3 4 5 6 7 8 9 10 11 12]\n",
      "append 13  len=13 cap=16 data=[1 2 3 4 5 6 7 8 9 10 11 12 13]\n",
      "append 14  len=14 cap=16 data=[1 2 3 4 5 6 7 8 9 10 11 12 13 14]\n",
      "append 15  len=15 cap=16 data=[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]\n",
      "append 16  len=16 cap=16 data=[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]\n",
      "append 17  len=17 cap=32 data=[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17]\n",
      "append 18  len=18 cap=32 data=[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18]\n",
      "append 19  len=19 cap=32 data=[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]\n",
      "append 20  len=20 cap=32 data=[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tvar s []int\n",
    "\tprintSlice(\"初始\", s)\n",
    "\n",
    "\tfor i := 1; i <= 20; i++ {\n",
    "\t\ts = append(s, i)\n",
    "\t\tprintSlice(fmt.Sprintf(\"append %2d\", i), s)\n",
    "\t}\n",
    "}\n",
    "\n",
    "// printSlice 输出切片的长度、容量和元素。\n",
    "func printSlice(msg string, s []int) {\n",
    "\tfmt.Printf(\"%-10s len=%2d cap=%2d data=%v\\n\", msg, len(s), cap(s), s)\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "go version go1.24.3 linux/amd64\n"
     ]
    }
   ],
   "source": [
    "! go version"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 32 切片内存共享"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始 base: [1 2 3 4 5]\n",
      "初始 s1: [1 2 3]\n",
      "初始 s2: [3 4 5]\n",
      "\n",
      "修改 s1[1] = 200\n",
      "base: [1 200 3 4 5]\n",
      "s1 : [1 200 3]\n",
      "s2 : [3 4 5]\n",
      "\n",
      "修改 s2[0] = 300\n",
      "base: [1 200 300 4 5]\n",
      "s1 : [1 200 300]\n",
      "s2 : [300 4 5]\n",
      "\n",
      "追加元素触发 s1 扩容\n",
      "append 后 s1: [1 200 300 999 888 777]\n",
      "此时 base: [1 200 300 4 5]\n",
      "此时 s2 : [300 4 5]\n",
      "\n",
      "扩容后修改 s1[0] = 1111\n",
      "base: [1 200 300 4 5]\n",
      "s1 : [1111 200 300 999 888 777]\n",
      "s2 : [300 4 5]\n"
     ]
    }
   ],
   "source": [
    "// 演示共享与扩容断连\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tbase := []int{1, 2, 3, 4, 5}\n",
    "\tfmt.Println(\"初始 base:\", base)\n",
    "\n",
    "\t// 通过切片表达式创建不同视图，共享同一底层数组\n",
    "\ts1 := base[:3]  // [1 2 3]\n",
    "\ts2 := base[2:]  // [3 4 5]\n",
    "\n",
    "\tfmt.Println(\"初始 s1:\", s1)\n",
    "\tfmt.Println(\"初始 s2:\", s2)\n",
    "\n",
    "\t// 修改 s1[1]，观察对 base、s2 的影响\n",
    "\ts1[1] = 200\n",
    "\tfmt.Println(\"\\n修改 s1[1] = 200\")\n",
    "\tfmt.Println(\"base:\", base)\n",
    "\tfmt.Println(\"s1 :\", s1)\n",
    "\tfmt.Println(\"s2 :\", s2)\n",
    "\n",
    "\t// 继续修改 s2[0]，观察相互影响\n",
    "\ts2[0] = 300\n",
    "\tfmt.Println(\"\\n修改 s2[0] = 300\")\n",
    "\tfmt.Println(\"base:\", base)\n",
    "\tfmt.Println(\"s1 :\", s1)\n",
    "\tfmt.Println(\"s2 :\", s2)\n",
    "\n",
    "\t// 触发 s1 扩容：当前 cap = len(base) = 5\n",
    "\t// 追加超出容量，Go 会为 s1 分配新底层数组\n",
    "\tfmt.Println(\"\\n追加元素触发 s1 扩容\")\n",
    "\ts1 = append(s1, 999, 888, 777)\n",
    "\tfmt.Println(\"append 后 s1:\", s1)\n",
    "\tfmt.Println(\"此时 base:\", base)\n",
    "\tfmt.Println(\"此时 s2 :\", s2)\n",
    "\n",
    "\t// 扩容后再修改 s1，不再影响 base/s2\n",
    "\ts1[0] = 1111\n",
    "\tfmt.Println(\"\\n扩容后修改 s1[0] = 1111\")\n",
    "\tfmt.Println(\"base:\", base)\n",
    "\tfmt.Println(\"s1 :\", s1)\n",
    "\tfmt.Println(\"s2 :\", s2)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 意外数据污染"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 中，多个切片可能共享同一块底层数组，只是持有不同的 “视图”（指针 + 长度 + 容量）。如果你：\n",
    "\n",
    "- 返回一个子切片给调用者，或\n",
    "- 在已有切片上 `append`\n",
    "\n",
    "就可能 **无意中** 让不同模块/函数共享了同一底层数组。一方修改数据，会影响另一方，造成“意外数据污染”。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "什么时候会发生？\n",
    "\n",
    "1. **返回子切片**\n",
    "    函数内部基于切片裁剪后返回，调用者拿到的子切片与函数内部的原切片共用底层数组。后续任一方修改内容，都会影响另一方。\n",
    "2. **在已有切片上 `append`**\n",
    "    如果容量足够，`append` 会复用原底层数组。所以两个持有同一底层数组的切片，只要其中一个 `append` 不触发扩容，就可能覆盖另一个看到的数据。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如何避免？\n",
    "\n",
    "- **使用 `copy` 创建独立副本**\n",
    "   在需要“安全”返回或保留的数据上做 `copy`，确保新切片拥有自己的底层数组。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "原始 data   : [10 20 30 40 50]\n",
      "共享 firstTwo: [10 20]\n",
      "\n",
      "append(999) 后的 firstTwo: [10 20 999]\n",
      "data 被污染                : [10 20 999 40 50]\n",
      "\n",
      "原始 data2     : [10 20 30 40 50]\n",
      "独立 firstTwoSafe: [10 20]\n",
      "\n",
      "append(999) 后的 firstTwoSafe: [10 20 999]\n",
      "data2 保持不变                 : [10 20 30 40 50]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// getFirstTwoUnsafe 返回前两个元素的子切片：共享底层数组\n",
    "func getFirstTwoUnsafe(source []int) []int {\n",
    "\treturn source[:2]\n",
    "}\n",
    "\n",
    "// getFirstTwoSafe 返回前两个元素的拷贝：拥有独立底层数组\n",
    "func getFirstTwoSafe(source []int) []int {\n",
    "\tresult := make([]int, 2)\n",
    "\tcopy(result, source[:2])\n",
    "\treturn result\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tdata := []int{10, 20, 30, 40, 50}\n",
    "\tfirstTwo := getFirstTwoUnsafe(data)\n",
    "\n",
    "\tfmt.Println(\"原始 data   :\", data)\n",
    "\tfmt.Println(\"共享 firstTwo:\", firstTwo)\n",
    "\n",
    "\t// 在 firstTwo 上执行 append（未超过 cap），仍写回原数组\n",
    "\tfirstTwo = append(firstTwo, 999)\n",
    "\tfmt.Println(\"\\nappend(999) 后的 firstTwo:\", firstTwo)\n",
    "\tfmt.Println(\"data 被污染                :\", data)\n",
    "\n",
    "\t// 修复：使用 copy 保护数据\n",
    "\tdata2 := []int{10, 20, 30, 40, 50}\n",
    "\tfirstTwoSafe := getFirstTwoSafe(data2)\n",
    "\n",
    "\tfmt.Println(\"\\n原始 data2     :\", data2)\n",
    "\tfmt.Println(\"独立 firstTwoSafe:\", firstTwoSafe)\n",
    "\n",
    "\t// 对安全切片 append，不会污染 data2\n",
    "\tfirstTwoSafe = append(firstTwoSafe, 999)\n",
    "\tfmt.Println(\"\\nappend(999) 后的 firstTwoSafe:\", firstTwoSafe)\n",
    "\tfmt.Println(\"data2 保持不变                 :\", data2)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 33 切片相关函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 函数/关键字              | 作用                                                   | 使用要点                                      |\n",
    "| ------------------------ | ------------------------------------------------------ | --------------------------------------------- |\n",
    "| `len(s)`                 | 返回切片当前长度（元素个数）。                         | 只读操作，O(1)。                              |\n",
    "| `cap(s)`                 | 返回切片当前容量（底层数组从起始位置可容纳的元素数）。 | 用于判断是否会触发扩容。                      |\n",
    "| `make([]T, len, cap)`    | 创建指定类型 `T` 的切片，预分配底层数组。              | `cap` 可选，省略时 `cap == len`。             |\n",
    "| `append(s, elements...)` | 向切片末尾追加元素，必要时自动扩容。                   | 可能返回新底层数组，记得接收返回值。          |\n",
    "| `copy(dst, src)`         | 将 `src` 的元素复制到 `dst`，返回复制的元素个数。      | `dst`、`src` 均为切片；复制长度为两者较小值。 |\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始 s: len=3 cap=5 data=[0 0 0]\n",
      "赋值后 s: [10 20 30]\n",
      "append 40 后: len=4 cap=5 data=[10 20 30 40]\n",
      "追加 3 个元素后: len=7 cap=10 data=[10 20 30 40 50 60 70]\n",
      "copy 到 clone，复制元素数=7 clone=[10 20 30 40 50 60 70]\n",
      "修改 clone[0]=999 后 clone=[999 20 30 40 50 60 70], 原 s=[10 20 30 40 50 60 70]\n",
      "子切片 sub = s[1:4]: len=3 cap=9 data=[20 30 40]\n",
      "修改 sub[0]=888 后 sub=[888 30 40], s=[10 888 30 40 50 60 70]\n",
      "删除索引 2 后 s=[10 888 40 50 60 70]\n",
      "排序后的 s=[10 40 50 60 70 888]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"sort\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\t// 使用 make 创建一个长度 3、容量 5 的切片\n",
    "\ts := make([]int, 3, 5)\n",
    "\tfmt.Printf(\"初始 s: len=%d cap=%d data=%v\\n\", len(s), cap(s), s)\n",
    "\n",
    "\t// 为初始元素赋值\n",
    "\ts[0], s[1], s[2] = 10, 20, 30\n",
    "\tfmt.Printf(\"赋值后 s: %v\\n\", s)\n",
    "\n",
    "\t// append 元素，不触发扩容\n",
    "\ts = append(s, 40)\n",
    "\tfmt.Printf(\"append 40 后: len=%d cap=%d data=%v\\n\", len(s), cap(s), s)\n",
    "\n",
    "\t// 再 append 多个元素，触发扩容（容量翻倍或按策略增长）\n",
    "\ts = append(s, 50, 60, 70)\n",
    "\tfmt.Printf(\"追加 3 个元素后: len=%d cap=%d data=%v\\n\", len(s), cap(s), s)\n",
    "\n",
    "\t// copy：克隆一个独立的切片\n",
    "\tclone := make([]int, len(s))\n",
    "\tn := copy(clone, s)\n",
    "\tfmt.Printf(\"copy 到 clone，复制元素数=%d clone=%v\\n\", n, clone)\n",
    "\n",
    "\t// 修改 clone，不影响原切片\n",
    "\tclone[0] = 999\n",
    "\tfmt.Printf(\"修改 clone[0]=999 后 clone=%v, 原 s=%v\\n\", clone, s)\n",
    "\n",
    "\t// 切片截取：共享底层数组\n",
    "\tsub := s[1:4]\n",
    "\tfmt.Printf(\"子切片 sub = s[1:4]: len=%d cap=%d data=%v\\n\", len(sub), cap(sub), sub)\n",
    "\n",
    "\t// 对 sub 修改会反映回 s（因为共享底层数组）\n",
    "\tsub[0] = 888\n",
    "\tfmt.Printf(\"修改 sub[0]=888 后 sub=%v, s=%v\\n\", sub, s)\n",
    "\n",
    "\t// 删除操作没有内建函数，惯用 append+切片实现\n",
    "\t// 删除 s 中索引 2 的元素\n",
    "\tindexToDelete := 2\n",
    "\ts = append(s[:indexToDelete], s[indexToDelete+1:]...)\n",
    "\tfmt.Printf(\"删除索引 %d 后 s=%v\\n\", indexToDelete, s)\n",
    "\n",
    "\t// sort.Slice 提供更灵活的排序\n",
    "\tsort.Slice(s, func(i, j int) bool { return s[i] < s[j] })\n",
    "\tfmt.Printf(\"排序后的 s=%v\\n\", s)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 标准库中处理切片的工具"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 包                   | 经典函数/特性                                                | 适用场景简述                                                 |\n",
    "| -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |\n",
    "| `sort`               | `sort.Ints`、`sort.Slice`、`sort.SliceStable`、`sort.Search` | 对任意类型的切片排序、稳定排序、按条件二分查找。             |\n",
    "| `slices`（Go 1.21+） | `slices.Sort`、`slices.Clone`、`slices.Compact`、`slices.DeleteFunc`、`slices.BinarySearch` | 针对泛型切片的常用操作：排序、克隆、去重、按谓词删除、二分查找等。 |\n",
    "| `bytes`              | `bytes.Split`、`bytes.TrimSpace`、`bytes.Contains` 等        | 对 `[]byte`（本质也是切片）进行分割、裁剪、匹配。            |\n",
    "| `strings`            | `strings.Fields`、`strings.Split`、`strings.Join` 等         | 将字符串分割成切片、或把切片重新拼接成字符串。               |\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "slices.Sort: [3 7 19 42 42]\n",
      "slices.Compact: [3 7 19 42]\n",
      "slices.BinarySearch(19): index=2 found=true\n",
      "\n",
      "sort.Slice (by score desc): [{Alice 95} {Carol 88} {Bob 72} {Dave 60}]\n",
      "sort.Search 分界点（<80）: index=2\n",
      "\n",
      "bytes.Split -> [\"alpha\" \"beta\" \"gamma\" \"delta\"]\n",
      "labels: [alpha beta gamma delta]\n",
      "slices.DeleteFunc (过滤包含 \"mm\"): [alpha beta delta]\n",
      "\n",
      "strings.Fields -> []string{\"go\", \"slices\", \"make\", \"life\", \"easier\"}\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"bytes\"\n",
    "\t\"fmt\"\n",
    "\t\"sort\"\n",
    "\t\"slices\"\n",
    "\t\"strings\"\n",
    ")\n",
    "\n",
    "type player struct {\n",
    "\tName  string\n",
    "\tScore int\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\t// ====== slices 包：排序、克隆、去重、二分查找 ======\n",
    "\trawScores := []int{42, 7, 19, 42, 3}\n",
    "\n",
    "\tsortedScores := slices.Clone(rawScores) // 克隆，避免修改原切片\n",
    "\tslices.Sort(sortedScores)\n",
    "\tfmt.Println(\"slices.Sort:\", sortedScores)\n",
    "\n",
    "\tuniqueScores := slices.Clone(sortedScores)\n",
    "\tuniqueScores = slices.Compact(uniqueScores) // 去除相邻重复元素\n",
    "\tfmt.Println(\"slices.Compact:\", uniqueScores)\n",
    "\n",
    "\tpos, found := slices.BinarySearch(uniqueScores, 19)\n",
    "\tfmt.Printf(\"slices.BinarySearch(19): index=%d found=%t\\n\", pos, found)\n",
    "\n",
    "\t// ====== sort 包：对结构体切片排序 + 二分查找定位阈值 ======\n",
    "\tplayers := []player{\n",
    "\t\t{Name: \"Alice\", Score: 95},\n",
    "\t\t{Name: \"Bob\", Score: 72},\n",
    "\t\t{Name: \"Carol\", Score: 88},\n",
    "\t\t{Name: \"Dave\", Score: 60},\n",
    "\t}\n",
    "\n",
    "\tsort.Slice(players, func(i, j int) bool {\n",
    "\t\treturn players[i].Score > players[j].Score // 按得分降序\n",
    "\t})\n",
    "\tfmt.Println(\"\\nsort.Slice (by score desc):\", players)\n",
    "\n",
    "\tthreshold := 80\n",
    "\tfirstBelow := sort.Search(len(players), func(i int) bool {\n",
    "\t\treturn players[i].Score < threshold // 切片已按得分降序\n",
    "\t})\n",
    "\tfmt.Printf(\"sort.Search 分界点（<%d）: index=%d\\n\", threshold, firstBelow)\n",
    "\n",
    "\t// ====== bytes 包：处理 []byte 切片 ======\n",
    "\tresponses := []byte(\"  alpha,beta,gamma,delta  \")\n",
    "\ttrimmed := bytes.TrimSpace(responses)\n",
    "\tchunks := bytes.Split(trimmed, []byte(\",\"))\n",
    "\tfmt.Printf(\"\\nbytes.Split -> %q\\n\", chunks)\n",
    "\n",
    "\t// 将 []byte 片段转换成字符串切片，便于进一步处理\n",
    "\tlabels := make([]string, len(chunks))\n",
    "\tfor i, chunk := range chunks {\n",
    "\t\tlabels[i] = string(chunk)\n",
    "\t}\n",
    "\tfmt.Println(\"labels:\", labels)\n",
    "\n",
    "\t// ====== slices.DeleteFunc + strings 包：切片过滤 ======\n",
    "\tlabels = slices.DeleteFunc(labels, func(s string) bool {\n",
    "\t\treturn strings.Contains(s, \"mm\") // 删除包含 \"mm\" 的项（即 \"gamma\"）\n",
    "\t})\n",
    "\tfmt.Println(\"slices.DeleteFunc (过滤包含 \\\"mm\\\"):\", labels)\n",
    "\n",
    "\twords := strings.Fields(\"go   slices make life easier\")\n",
    "\tfmt.Printf(\"\\nstrings.Fields -> %#v\\n\", words)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 34 字符串及常用操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Go 中的字符串(`string`)是**以只读方式存储的 UTF-8 字节序列**，具备以下特点：\n",
    "- **不可变**：一旦创建无法原地修改，涉及“修改”的操作通常返回新字符串。\n",
    "- **UTF-8 编码**：默认按字节长度计数，注意对多字节（非 ASCII）字符的处理。\n",
    "- **底层实现**：由指向只读字节数组的指针和长度组成。\n",
    "- **零值**：空字符串 `\"\"`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 常见注意事项\n",
    "\n",
    "- **不可原地修改**：对字符串的“修改”实质是创建新字符串，频繁拼接建议使用 `strings.Builder` 或 `bytes.Buffer`。\n",
    "- **索引单位是字节**：中文等多字节字符需特别注意，转换成 `[]rune` 或使用 `range` 遍历字符。\n",
    "- **空字符串与 nil**：字符串类型不存在 `nil` 值，零值为 `\"\"`。\n",
    "- **与切片转换的副本**：`[]byte(s)` 会复制原字符串内容，新切片修改不会影响原字符串。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 字面量形式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Go!\n",
      "多行文本：\n",
      "第一行\n",
      "第二行\\t（这里不会转义）\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "    // 普通常量\n",
    "    s1 := \"Hello, Go!\"\n",
    "    // 原始字符串字面量（反引号），保留换行和特殊字符\n",
    "    s2 := `多行文本：\n",
    "第一行\n",
    "第二行\\t（这里不会转义）`\n",
    "    \n",
    "    fmt.Println(s1)\n",
    "    fmt.Println(s2)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 常见基础操作"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 获取长度与索引"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "13\n",
      "72\n"
     ]
    }
   ],
   "source": [
    "// len 以 字节 为单位，中文等多字节字符要使用 utf8.RuneCountInString 等函数获取字符数。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "    s := \"Hello, 世界\"\n",
    "    fmt.Println(len(s))     // 13：按字节计数\n",
    "    fmt.Println(s[0])       // 72：第一个字节的值\n",
    "\n",
    "    // 字符串不可变，不能直接 s[0] = 'h'\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 字符串连接"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Go!\n",
      "Hello -> Go\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "    s1 := \"Hello\"\n",
    "    s2 := \"Go\"\n",
    "    s3 := s1 + \", \" + s2 + \"!\"\n",
    "    fmt.Println(s3)\n",
    "\n",
    "    // 使用 fmt.Sprintf\n",
    "    s4 := fmt.Sprintf(\"%s -> %s\", s1, s2)\n",
    "    fmt.Println(s4)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 使用 `strings.Builder` 提升性能"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Builder!\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "    \"fmt\"\n",
    "    \"strings\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "    var builder strings.Builder\n",
    "    builder.WriteString(\"Hello\")\n",
    "    builder.WriteString(\", \")\n",
    "    builder.WriteString(\"Builder!\")\n",
    "    result := builder.String()\n",
    "    fmt.Println(result)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 字符串遍历"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "byte 0: 47\n",
      "byte 1: 6f\n",
      "byte 2: e8\n",
      "byte 3: af\n",
      "byte 4: ad\n",
      "byte 5: e8\n",
      "byte 6: a8\n",
      "byte 7: 80\n",
      "\n",
      "index 0: rune G (U+0047)\n",
      "index 1: rune o (U+006F)\n",
      "index 2: rune 语 (U+8BED)\n",
      "index 5: rune 言 (U+8A00)\n",
      "字符数： 4\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "    \"fmt\"\n",
    "    \"unicode/utf8\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "    s := \"Go语言\"\n",
    "\n",
    "    // 按字节遍历\n",
    "    for i := 0; i < len(s); i++ {\n",
    "        fmt.Printf(\"byte %d: %x\\n\", i, s[i])\n",
    "    }\n",
    "\n",
    "    fmt.Println()\n",
    "\n",
    "    // 按 rune 遍历\n",
    "    for idx, r := range s {\n",
    "        fmt.Printf(\"index %d: rune %c (U+%04X)\\n\", idx, r, r)\n",
    "    }\n",
    "\n",
    "    fmt.Println(\"字符数：\", utf8.RuneCountInString(s))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## strings 包常用函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true\n",
      "true\n",
      "true\n",
      "3\n",
      "8\n",
      "  GO,GO,语言,GO  \n",
      "Go,Go,语言,Go\n",
      "[Go Go 语言 Go]\n",
      "Go | Go | 语言 | Go\n",
      "  Golang,Golang,语言,Go  \n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "    \"fmt\"\n",
    "    \"strings\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "    s := \"  Go,Go,语言,Go  \"\n",
    "\n",
    "    fmt.Println(strings.Contains(s, \"语言\"))         // true\n",
    "    fmt.Println(strings.HasPrefix(s, \"  Go\"))        // true\n",
    "    fmt.Println(strings.HasSuffix(s, \"Go  \"))        // true\n",
    "\n",
    "    fmt.Println(strings.Count(s, \"Go\"))              // 3\n",
    "    fmt.Println(strings.Index(s, \"语言\"))            // 10（按字节索引）\n",
    "\n",
    "    fmt.Println(strings.ToUpper(s))                  // \"  GO,GO,语言,GO  \"\n",
    "    fmt.Println(strings.TrimSpace(s))                // \"Go,Go,语言,Go\"\n",
    "\n",
    "    parts := strings.Split(strings.TrimSpace(s), \",\")\n",
    "    fmt.Println(parts)                               // [Go Go 语言 Go]\n",
    "\n",
    "    joined := strings.Join(parts, \" | \")\n",
    "    fmt.Println(joined)                              // \"Go | Go | 语言 | Go\"\n",
    "\n",
    "    replaced := strings.Replace(s, \"Go\", \"Golang\", 2)\n",
    "    fmt.Println(replaced)                            // \"  Golang,Golang,语言,Go  \"\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## strconv 包：字符串与基本类型转换"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42\n",
      "2025\n",
      "3.1415\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "    \"fmt\"\n",
    "    \"strconv\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "    i, err := strconv.Atoi(\"42\")\n",
    "    if err != nil {\n",
    "        panic(err)\n",
    "    }\n",
    "    fmt.Println(i) // 42\n",
    "\n",
    "    s := strconv.Itoa(2025)\n",
    "    fmt.Println(s) // \"2025\"\n",
    "\n",
    "    f, err := strconv.ParseFloat(\"3.1415\", 64)\n",
    "    if err != nil {\n",
    "        panic(err)\n",
    "    }\n",
    "    fmt.Println(f) // 3.1415\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## bytes 包：处理可变字节切片"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, Go!\n",
      "你好，Buffer\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "    \"bytes\"\n",
    "    \"fmt\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "    b := []byte(\"Hello\")\n",
    "    b = append(b, []byte(\", Go!\")...)\n",
    "    fmt.Println(string(b)) // 转回 string\n",
    "\n",
    "    var buf bytes.Buffer\n",
    "    buf.WriteString(\"你好，\")\n",
    "    buf.Write([]byte(\"Buffer\"))\n",
    "    fmt.Println(buf.String())\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## UTF-8 与 rune 处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "字节长度: 9\n",
      "字符个数: 3\n",
      "第二个字符: 谢\n",
      "谢谢你\n"
     ]
    }
   ],
   "source": [
    "// 使用 unicode/utf8 包解析多字节字符，或 []rune 切片实现按字符索引。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "    \"fmt\"\n",
    "    \"unicode/utf8\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "    s := \"谢谢你\"\n",
    "    fmt.Println(\"字节长度:\", len(s))\n",
    "    fmt.Println(\"字符个数:\", utf8.RuneCountInString(s))\n",
    "\n",
    "    runes := []rune(s)\n",
    "    fmt.Println(\"第二个字符:\", string(runes[1])) // \"谢\"\n",
    "\n",
    "    // 转换回字符串\n",
    "    fmt.Println(string(runes))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 35 数据类型转换"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 使用 `T(value)` 进行显式转换，确保类型兼容并注意溢出。\n",
    "- 接口类型用断言 `x.(T)` 或 `type switch` 获取动态类型。\n",
    "- `strconv`、`encoding/json` 等库简化常见转换任务。\n",
    "- 避免滥用 `unsafe`，仅在性能/底层需求时使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 常见误区与注意事项\n",
    "\n",
    "1. **无隐式转换**：`int`、`int32`、`float32` 等都需要显式转换。\n",
    "2. **溢出行为**：整数转换不会检查溢出，需自行确保范围。\n",
    "3. **字符串转换复制**：`string` ↔ `[]byte` 会复制数据，频繁转换需注意性能。\n",
    "4. **类型断言只能用于接口类型**：非接口变量不能直接使用断言。\n",
    "5. **不可拷贝结构**：含互斥锁、`strings.Builder` 等类型禁止拷贝；赋值时需小心。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 中，类型转换分为两大类：\n",
    "\n",
    "1. **显式类型转换（Type Conversion）**：在编译时由程序员主动转换，使用语法 `T(value)`。\n",
    "2. **类型断言（Type Assertion）**：针对接口类型，在运行时判断动态类型。\n",
    "\n",
    "Go 不支持隐式转换，任何不同类型的值赋给变量前都必须显式转换。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 显式类型转换"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 数值类型之间的转换"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42 42 42\n",
      "-1\n"
     ]
    }
   ],
   "source": [
    "// Go 允许在整数、浮点、复数之间显式转换。需注意范围溢出与精度丢失。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "    var i int = 42\n",
    "    var f float64 = float64(i) // int -> float64\n",
    "    var u uint = uint(f)       // float64 -> uint（截断小数部分）\n",
    "\n",
    "    fmt.Println(i, f, u)\n",
    "\n",
    "    // 溢出示例（编译不会报错，但结果不可信）\n",
    "    var big int64 = 1<<63 - 1\n",
    "    fmt.Println(int32(big)) // 溢出，结果未定义（会发生截断）\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 字节、字符与字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[71 111 232 175 173 232 168 128]\n",
      "[71 111 35821 35328]\n",
      "Go语言\n",
      "Go语言\n",
      "谢\n"
     ]
    }
   ],
   "source": [
    "// byte 是 uint8 的别名，rune 是 int32 的别名。\n",
    "// 字符串与字节切片转换会复制数据。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "    s := \"Go语言\"\n",
    "    bytes := []byte(s) // string -> []byte（复制）\n",
    "    runes := []rune(s) // string -> []rune（按 Unicode 码点）\n",
    "\n",
    "    fmt.Println(bytes)          // [71 111 232 ...]\n",
    "    fmt.Println(runes)          // [71 111 35821 35328]\n",
    "    fmt.Println(string(bytes))  // []byte -> string\n",
    "    fmt.Println(string(runes))  // []rune -> string\n",
    "\n",
    "    var r rune = '谢'\n",
    "    fmt.Println(string(r))      // rune -> string\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 布尔型与数值/字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "// Go 不支持布尔与数值的直接互转。如需转换，需手动逻辑处理。\n",
    "\n",
    "b := true\n",
    "i := 0\n",
    "if b {\n",
    "    i = 1\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类型断言（接口与动态类型）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当变量的静态类型是接口时，可使用 `x.(T)` 将其断言为具体类型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 单值断言（可能 panic）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "string 断言成功: hello\n",
      "即将触发 panic...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "panic: interface conversion: interface {} is string, not float64\n",
      "\n",
      "goroutine 1 [running]:\n",
      "main.main()\n",
      "\t \u001b[7m[[ Cell [54] Line 18 ]]\u001b[0m /tmp/gonb_73417de5/main.go:254 +0xcc\n",
      "exit status 2\n"
     ]
    }
   ],
   "source": [
    "// 使用 value := iface.(T) 的形式从接口中取出具体类型。\n",
    "// 若动态类型不是 T，会触发运行时 panic。\n",
    "// 示例中故意展示 panic 行为，实际运行时建议注释掉错误断言部分。\n",
    "// 如需避免 panic，可改用“双值断言”或注释掉错误断言。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tvar i interface{} = \"hello\"\n",
    "\n",
    "\t// 正确断言\n",
    "\ts := i.(string)\n",
    "\tfmt.Println(\"string 断言成功:\", s)\n",
    "\n",
    "\t// 错误断言（会 panic）\n",
    "\tfmt.Println(\"即将触发 panic...\")\n",
    "\tf := i.(float64) // panic: interface conversion\n",
    "\tfmt.Println(f)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 双值断言（安全）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "不是 float64，ok = false\n",
      "string: hello\n"
     ]
    }
   ],
   "source": [
    "// 使用 value, ok := iface.(T) 获取断言结果与布尔状态。\n",
    "// 当断言失败时，不会 panic，而是返回零值与 false。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tvar i interface{} = \"hello\"\n",
    "\n",
    "\tif f, ok := i.(float64); ok {\n",
    "\t\tfmt.Println(\"float64:\", f)\n",
    "\t} else {\n",
    "\t\tfmt.Println(\"不是 float64，ok =\", ok)\n",
    "\t}\n",
    "\n",
    "\tif s, ok := i.(string); ok {\n",
    "\t\tfmt.Println(\"string:\", s)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 接口断言为接口"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始读取: \n",
      "通过 Writer 写入后读取: Hello from Writer\n"
     ]
    }
   ],
   "source": [
    "// 断言不仅可以转为具体类型，也可转为其他接口。\n",
    "// 只要动态类型实现了目标接口即可成功。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Reader interface {\n",
    "\tRead() string\n",
    "}\n",
    "\n",
    "type Writer interface {\n",
    "\tWrite(string)\n",
    "}\n",
    "\n",
    "type myType struct {\n",
    "\tbuf string\n",
    "}\n",
    "\n",
    "func (m *myType) Read() string {\n",
    "\treturn m.buf\n",
    "}\n",
    "\n",
    "func (m *myType) Write(s string) {\n",
    "\tm.buf = s\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvar r Reader = &myType{}\n",
    "\tfmt.Println(\"初始读取:\", r.Read())\n",
    "\n",
    "\tif w, ok := r.(Writer); ok {\n",
    "\t\tw.Write(\"Hello from Writer\")\n",
    "\t\tfmt.Println(\"通过 Writer 写入后读取:\", r.Read())\n",
    "\t} else {\n",
    "\t\tfmt.Println(\"对象未实现 Writer 接口\")\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类型转换、断言与 type switch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "int: 42\n",
      "string: Go\n",
      "implements String(): custom stringer\n",
      "unknown type (float64): 3.14\n"
     ]
    }
   ],
   "source": [
    "// type switch 是大量断言场景的优雅写法。\n",
    "// 可根据动态类型执行不同分支。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "type custom struct{}\n",
    "\n",
    "func (c custom) String() string {\n",
    "\treturn \"custom stringer\"\n",
    "}\n",
    "\n",
    "func describe(i interface{}) {\n",
    "\tswitch v := i.(type) {\n",
    "\tcase int:\n",
    "\t\tfmt.Println(\"int:\", v)\n",
    "\tcase string:\n",
    "\t\tfmt.Println(\"string:\", v)\n",
    "\tcase fmt.Stringer:\n",
    "\t\tfmt.Println(\"implements String():\", v.String())\n",
    "\tdefault:\n",
    "\t\tfmt.Printf(\"unknown type (%T): %v\\n\", v, v)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tdescribe(42)\n",
    "\tdescribe(\"Go\")\n",
    "\tdescribe(custom{})\n",
    "\tdescribe(3.14)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 常见标准库辅助转换"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "-   strconv ：字符串与基础类型互转 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Atoi(\"42\") = 42\n",
      "Itoa(2025) = 2025\n",
      "ParseFloat(\"3.14\", 64) = 3.14\n",
      "ParseBool(\"true\") = true\n"
     ]
    }
   ],
   "source": [
    "// strconv 是处理数值与字符串转换的标准库。\n",
    "// 常用函数包括 Atoi、Itoa、ParseFloat、ParseBool 等。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"strconv\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\t// string -> int\n",
    "\ti, err := strconv.Atoi(\"42\")\n",
    "\tif err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tfmt.Println(\"Atoi(\\\"42\\\") =\", i)\n",
    "\n",
    "\t// int -> string\n",
    "\ts := strconv.Itoa(2025)\n",
    "\tfmt.Println(\"Itoa(2025) =\", s)\n",
    "\n",
    "\t// string -> float\n",
    "\tf, err := strconv.ParseFloat(\"3.14\", 64)\n",
    "\tif err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tfmt.Println(\"ParseFloat(\\\"3.14\\\", 64) =\", f)\n",
    "\n",
    "\t// string -> bool\n",
    "\tb, err := strconv.ParseBool(\"true\")\n",
    "\tif err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tfmt.Println(\"ParseBool(\\\"true\\\") =\", b)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `encoding/json` 序列化 / 反序列化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "反序列化结果: {Name:Alice Age:30}\n",
      "重新序列化: {\"name\":\"Alice\",\"age\":30}\n"
     ]
    }
   ],
   "source": [
    "// JSON 编解码会自动根据结构体字段完成类型转换。\n",
    "// 需要注意结构体标签与字段匹配。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"encoding/json\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "type Person struct {\n",
    "\tName string `json:\"name\"`\n",
    "\tAge  int    `json:\"age\"`\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tjsonStr := `{\"name\": \"Alice\", \"age\": 30}`\n",
    "\n",
    "\tvar p Person\n",
    "\tif err := json.Unmarshal([]byte(jsonStr), &p); err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tfmt.Printf(\"反序列化结果: %+v\\n\", p)\n",
    "\n",
    "\tout, err := json.Marshal(p)\n",
    "\tif err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tfmt.Println(\"重新序列化:\", string(out))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 指针与自定义类型转换"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 自定义类型与基础类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a (int)   = 10, 类型: int\n",
      "b (MyInt) = 10, 类型: main.MyInt\n",
      "c (int)   = 10, 类型: int\n"
     ]
    }
   ],
   "source": [
    "// 自定义类型（基于基础类型）与基础类型之间可互相显式转换。\n",
    "// 转换不会改变底层数据，但类型不同必须显式声明。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type MyInt int\n",
    "\n",
    "func main() {\n",
    "\tvar a int = 10\n",
    "\n",
    "\t// int -> MyInt\n",
    "\tvar b MyInt = MyInt(a)\n",
    "\n",
    "\t// MyInt -> int\n",
    "\tvar c int = int(b)\n",
    "\n",
    "\tfmt.Printf(\"a (int)   = %d, 类型: %T\\n\", a, a)\n",
    "\tfmt.Printf(\"b (MyInt) = %d, 类型: %T\\n\", b, b)\n",
    "\tfmt.Printf(\"c (int)   = %d, 类型: %T\\n\", c, c)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 结构体与指针"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a (A) = {X:5}, 类型: main.A\n",
      "b (B) = {X:5}, 类型: main.B\n",
      "bPtr 指向的值: {X:5}\n"
     ]
    }
   ],
   "source": [
    "// 结构体类型之间的转换需要字段与类型完全一致，不能自动转换；指针类型转换亦需兼容。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type A struct {\n",
    "\tX int\n",
    "}\n",
    "\n",
    "type B struct {\n",
    "\tX int\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\ta := A{X: 5}\n",
    "\n",
    "\t// 结构体之间字段完全一致，可以显式转换\n",
    "\tb := B(a)\n",
    "\n",
    "\tfmt.Printf(\"a (A) = %+v, 类型: %T\\n\", a, a)\n",
    "\tfmt.Printf(\"b (B) = %+v, 类型: %T\\n\", b, b)\n",
    "\n",
    "\t// 指针转换：先把 *A 转为 A，再显式转换为 B，最后取地址\n",
    "\taPtr := &a\n",
    "\tbPtr := (*B)(unsafe.Pointer(aPtr)) // 需要 unsafe，见下个示例\n",
    "\tfmt.Printf(\"bPtr 指向的值: %+v\\n\", *bPtr)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## unsafe 包与反射"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `unsafe` 提供对底层内存的直接访问能力，可在不同类型的指针之间互转，但可能破坏类型安全，使用时要非常谨慎。\n",
    "- `reflect` 允许在运行时检查、修改值的类型信息，常用于框架、序列化等需要动态处理的场景，但会带来性能开销。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "int: 0x1020304\n",
      "bytes: [4 3 2 1 0 0 0 0]（长度 8）\n"
     ]
    }
   ],
   "source": [
    "// 使用 unsafe 进行底层指针转换与内存视图\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"unsafe\"\n",
    ")\n",
    "\n",
    "func IntToBytes(i int) []byte {\n",
    "\tsize := unsafe.Sizeof(i)\n",
    "\treturn unsafe.Slice((*byte)(unsafe.Pointer(&i)), size)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tnum := 0x01020304\n",
    "\tbytes := IntToBytes(num)\n",
    "\n",
    "\tfmt.Printf(\"int: 0x%X\\n\", num)\n",
    "\tfmt.Printf(\"bytes: %v（长度 %d）\\n\", bytes, len(bytes))\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "值: 42         | 动态类型: int                  | 可取地址: false\n",
      "值: Go         | 动态类型: string               | 可取地址: false\n",
      "值: 3.14       | 动态类型: float64              | 可取地址: false\n",
      "值: {Alice     } | 动态类型: struct { Name string } | 可取地址: false\n"
     ]
    }
   ],
   "source": [
    "// 使用 reflect 检查动态类型信息\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"reflect\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tvalues := []interface{}{42, \"Go\", 3.14, struct{ Name string }{\"Alice\"}}\n",
    "\n",
    "\tfor _, v := range values {\n",
    "\t\tt := reflect.TypeOf(v)\n",
    "\t\tval := reflect.ValueOf(v)\n",
    "\n",
    "\t\tfmt.Printf(\"值: %-10v | 动态类型: %-20s | 可取地址: %v\\n\", v, t, val.CanAddr())\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 36 HashTable的实现原理，map扩容与遍历"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HashTable"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "哈希表（Hash Table）是一种基于 **键（Key）与值（Value）直接映射** 的数据结构，核心思想是通过哈希函数将键映射到数组索引，从而实现接近常数时间的查找、插入和删除。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "核心组件\n",
    "\n",
    "1. **哈希函数**：`hash(key)` 将键转为整数下标。好的哈希函数需要：\n",
    "   - *快速计算*：避免成为瓶颈。\n",
    "   - *均匀分布*：减少碰撞概率。\n",
    "   - *稳定性*：同一键的哈希值必须一致。\n",
    "2. **桶（Bucket）数组**：哈希函数输出被映射到桶上。每个桶可能存放一个或多个键值对。\n",
    "3. **碰撞处理**：\n",
    "   - **链地址法（拉链法）**：每个桶内维护链表或动态数组，冲突的元素存放在同一桶的链表中。\n",
    "   - **开放寻址法**：当冲突发生，按探测序列（线性探测、二次探测等）寻找下一个空桶。\n",
    "4. **负载因子（Load Factor）**：`loadFactor = 元素数量 / 桶的数量`。当负载因子过高时，需要扩容以保持效率。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "工作流程\n",
    "\n",
    "- **插入**：通过哈希函数定位桶 → 若冲突，采用碰撞策略 → 插入到桶中。\n",
    "- **查找**：定位桶 → 顺着桶内数据结构查找匹配的键。\n",
    "- **删除**：定位桶 → 删除指定键值对 → 维护桶内结构完整性。\n",
    "- **扩容**：当负载因子达到阈值时，分配更大数组并将原数据重新哈希（Rehash）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## map"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Go 的 `map` 使用 **改进的哈希表** 实现，其特点包括："
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "数据结构要点\n",
    "- `hmap` 结构包含 *桶数组指针*、*已使用的键值数量*、*负载因子* 及 *哈希种子* 等。\n",
    "- 每个桶（bucket）可存放最多 8 个键值对。当桶满时会发生 **溢出桶（overflow bucket）**。\n",
    "- Go 使用 **随机哈希种子** 增强安全性，防止利用碰撞发起的攻击。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "扩容机制\n",
    "\n",
    "- 当负载因子超过阈值（约为 6.5）或出现大量溢出桶时，触发扩容。\n",
    "- 扩容分两种情况：\n",
    "  1. **等量扩容（same size rehash）**：仅清理溢出桶，数据迁移到现有桶。\n",
    "  2. **翻倍扩容（grow / rehash）**：桶数量翻倍，键值对迁移到新数组。\n",
    "- 扩容与迁移采用 **渐进式 rehash**：在后续操作中逐步迁移部分桶，而不是一次性完成，减轻停顿。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "遍历顺序\n",
    "\n",
    "- Go 的 `map` 遍历顺序**每次都不固定**，原因在于：\n",
    "  - 迭代前随机选择起始桶。\n",
    "  - 遍历时遇到尚未迁移的旧桶时，会触发一次增量迁移。\n",
    "- 因此 **禁止依赖遍历顺序**，若需要保证顺序要额外处理（例如提取键后排序）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Go 内置 map 的行为演示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==== Go 内置 map 操作演示 ====\n",
      "初始 map: map[key-00:0 key-01:1 key-02:4 key-03:9 key-04:16 key-05:25 key-06:36 key-07:49 key-08:64 key-09:81]\n",
      "查找 key-05: value=25, ok=true\n",
      "删除 key-03 后: map[key-00:0 key-01:1 key-02:4 key-04:16 key-05:25 key-06:36 key-07:49 key-08:64 key-09:81]\n",
      "多次遍历 map（顺序会随机化）:\n",
      "第 1 次: key-09=81 key-00=0 key-01=1 key-02=4 key-04=16 key-05=25 key-08=64 key-06=36 key-07=49 \n",
      "第 2 次: key-04=16 key-05=25 key-08=64 key-06=36 key-07=49 key-09=81 key-00=0 key-01=1 key-02=4 \n",
      "第 3 次: key-06=36 key-07=49 key-09=81 key-00=0 key-01=1 key-02=4 key-04=16 key-05=25 key-08=64 \n",
      "\n",
      "模拟大量写入触发扩容（观察长度变化）\n",
      "插入 2 万项后，map 长度: 19999\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"math/rand\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"==== Go 内置 map 操作演示 ====\")\n",
    "\trand.Seed(time.Now().UnixNano())\n",
    "\n",
    "\tm := make(map[string]int)\n",
    "\n",
    "\t// 插入\n",
    "\tfor i := 0; i < 10; i++ {\n",
    "\t\tkey := fmt.Sprintf(\"key-%02d\", i)\n",
    "\t\tm[key] = i * i\n",
    "\t}\n",
    "\n",
    "\tfmt.Println(\"初始 map:\", m)\n",
    "\n",
    "\t// 查找\n",
    "\tval, ok := m[\"key-05\"]\n",
    "\tfmt.Printf(\"查找 key-05: value=%d, ok=%v\\n\", val, ok)\n",
    "\n",
    "\t// 删除\n",
    "\tdelete(m, \"key-03\")\n",
    "\tfmt.Println(\"删除 key-03 后:\", m)\n",
    "\n",
    "\t// 遍历顺序（多次迭代，顺序不同）\n",
    "\tfmt.Println(\"多次遍历 map（顺序会随机化）:\")\n",
    "\tfor iter := 1; iter <= 3; iter++ {\n",
    "\t\tfmt.Printf(\"第 %d 次: \", iter)\n",
    "\t\tfor k, v := range m {\n",
    "\t\t\tfmt.Printf(\"%s=%d \", k, v)\n",
    "\t\t}\n",
    "\t\tfmt.Println()\n",
    "\t}\n",
    "\n",
    "\t// 扩容模拟（通过插入大量元素）\n",
    "\tfmt.Println(\"\\n模拟大量写入触发扩容（观察长度变化）\")\n",
    "\tfor i := 10; i < 20000; i++ {\n",
    "\t\tkey := fmt.Sprintf(\"bulk-%d\", i)\n",
    "\t\tm[key] = i\n",
    "\t}\n",
    "\tfmt.Println(\"插入 2 万项后，map 长度:\", len(m))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 37 不定长参数和函数递归"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 不定长参数（Variadic Function）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Go 使用语法 `func name(params ...T)` 声明 **同类型的不定长参数**。\n",
    "- 调用时可传入零个或多个该类型的参数；也可以传递一个切片，使用 `fn(slice...)` 展开。\n",
    "- 在函数内部，不定长参数被视为同类型的切片，可以按切片方式遍历、操作。\n",
    "- 常见用途：求和、字符串拼接、日志格式化等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==== Variadic Function 示例 ====\n",
      "sum(1, 2, 3, 4)        = 10\n",
      "sum()                  = 0\n",
      "sum(values...)         = 60\n",
      "joinStrings(\"-\", \"Go\", \"Lang\") = Go-Lang\n",
      "joinStrings(\" \", words...)  = variadic functions are flexible\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"strings\"\n",
    ")\n",
    "\n",
    "// sum 计算任意数量的整数之和\n",
    "func sum(nums ...int) int {\n",
    "\ttotal := 0\n",
    "\tfor _, n := range nums {\n",
    "\t\ttotal += n\n",
    "\t}\n",
    "\treturn total\n",
    "}\n",
    "\n",
    "// joinStrings 使用可变参数拼接字符串\n",
    "func joinStrings(sep string, parts ...string) string {\n",
    "\treturn strings.Join(parts, sep)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"==== Variadic Function 示例 ====\")\n",
    "\n",
    "\t// 直接传多个参数\n",
    "\tfmt.Println(\"sum(1, 2, 3, 4)        =\", sum(1, 2, 3, 4))\n",
    "\n",
    "\t// 传空参数\n",
    "\tfmt.Println(\"sum()                  =\", sum())\n",
    "\n",
    "\t// 将切片展开传入\n",
    "\tvalues := []int{10, 20, 30}\n",
    "\tfmt.Println(\"sum(values...)         =\", sum(values...))\n",
    "\n",
    "\t// 字符串拼接示例\n",
    "\tfmt.Println(`joinStrings(\"-\", \"Go\", \"Lang\") =`, joinStrings(\"-\", \"Go\", \"Lang\"))\n",
    "\n",
    "\t// 传入切片展开\n",
    "\twords := []string{\"variadic\", \"functions\", \"are\", \"flexible\"}\n",
    "\tfmt.Println(\"joinStrings(\\\" \\\", words...)  =\", joinStrings(\" \", words...))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数递归（Recursion）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "递归函数是通过 **函数调用自身** 实现重复计算或层级遍历的技巧。\n",
    "\n",
    "关键要素：\n",
    "\n",
    "1. **终止条件**（Base Case）：防止无限递归。\n",
    "2. **递归调用**（Recursive Case）：问题拆分成更小子问题。\n",
    "\n",
    "典型应用：数学问题（阶乘、斐波那契）、分治算法（归并排序）、树/图遍历等。\n",
    "\n",
    "Go 支持递归调用，但要注意栈深度与性能，避免过深导致栈溢出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==== Recursion 示例 ====\n",
      "factorial(0) = 1\n",
      "factorial(1) = 1\n",
      "factorial(2) = 2\n",
      "factorial(3) = 6\n",
      "factorial(4) = 24\n",
      "factorial(5) = 120\n",
      "\n",
      "fibonacci(0) = 0\n",
      "fibonacci(1) = 1\n",
      "fibonacci(2) = 1\n",
      "fibonacci(3) = 2\n",
      "fibonacci(4) = 3\n",
      "fibonacci(5) = 5\n",
      "fibonacci(6) = 8\n",
      "fibonacci(7) = 13\n",
      "fibonacci(8) = 21\n",
      "fibonacci(9) = 34\n",
      "fibonacci(10) = 55\n",
      "\n",
      "二叉树先序遍历结果: 1 2 4 5 3 6 \n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// factorial 使用递归计算 n 的阶乘\n",
    "func factorial(n int) int {\n",
    "\tif n < 0 {\n",
    "\t\tpanic(\"factorial: n must be >= 0\")\n",
    "\t}\n",
    "\tif n == 0 || n == 1 {\n",
    "\t\treturn 1 // 终止条件\n",
    "\t}\n",
    "\treturn n * factorial(n-1) // 递归调用\n",
    "}\n",
    "\n",
    "// fibonacci 使用递归计算第 n 个斐波那契数\n",
    "func fibonacci(n int) int {\n",
    "\tif n < 0 {\n",
    "\t\tpanic(\"fibonacci: n must be >= 0\")\n",
    "\t}\n",
    "\tif n == 0 {\n",
    "\t\treturn 0\n",
    "\t}\n",
    "\tif n == 1 {\n",
    "\t\treturn 1\n",
    "\t}\n",
    "\treturn fibonacci(n-1) + fibonacci(n-2)\n",
    "}\n",
    "\n",
    "// TreeNode 定义二叉树节点\n",
    "type TreeNode struct {\n",
    "\tVal   int\n",
    "\tLeft  *TreeNode\n",
    "\tRight *TreeNode\n",
    "}\n",
    "\n",
    "// preorderTraversal 对二叉树进行先序遍历\n",
    "func preorderTraversal(root *TreeNode) {\n",
    "\tif root == nil {\n",
    "\t\treturn // 终止条件：空节点\n",
    "\t}\n",
    "\tfmt.Printf(\"%d \", root.Val)  // 处理当前节点\n",
    "\tpreorderTraversal(root.Left) // 遍历左子树\n",
    "\tpreorderTraversal(root.Right) // 遍历右子树\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"==== Recursion 示例 ====\")\n",
    "\n",
    "\t// 阶乘\n",
    "\tfor i := 0; i <= 5; i++ {\n",
    "\t\tfmt.Printf(\"factorial(%d) = %d\\n\", i, factorial(i))\n",
    "\t}\n",
    "\n",
    "\tfmt.Println()\n",
    "\n",
    "\t// 斐波那契\n",
    "\tfor i := 0; i <= 10; i++ {\n",
    "\t\tfmt.Printf(\"fibonacci(%d) = %d\\n\", i, fibonacci(i))\n",
    "\t}\n",
    "\n",
    "\tfmt.Println()\n",
    "\n",
    "\t// 二叉树先序遍历\n",
    "\t// 构建示例树：\n",
    "\t//        1\n",
    "\t//      /   \\\n",
    "\t//     2     3\n",
    "\t//    / \\     \\\n",
    "\t//   4   5     6\n",
    "\troot := &TreeNode{\n",
    "\t\tVal: 1,\n",
    "\t\tLeft: &TreeNode{\n",
    "\t\t\tVal: 2,\n",
    "\t\t\tLeft: &TreeNode{Val: 4},\n",
    "\t\t\tRight: &TreeNode{Val: 5},\n",
    "\t\t},\n",
    "\t\tRight: &TreeNode{\n",
    "\t\t\tVal: 3,\n",
    "\t\t\tRight: &TreeNode{Val: 6},\n",
    "\t\t},\n",
    "\t}\n",
    "\n",
    "\tfmt.Print(\"二叉树先序遍历结果: \")\n",
    "\tpreorderTraversal(root)\n",
    "\tfmt.Println()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 38 时间类型及其格式化"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 核心时间类型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 类型            | 包     | 概述                                              |\n",
    "| --------------- | ------ | ------------------------------------------------- |\n",
    "| `time.Time`     | `time` | 表示一个具体的时间点，包含日期、时区、纳秒精度。  |\n",
    "| `time.Duration` | `time` | 表示两个时间点间隔的长度，底层是 `int64` 纳秒数。 |\n",
    "| `time.Location` | `time` | 表示时区信息，可用于在 `time.Time` 上变换时区。   |\n",
    "| `time.Ticker`   | `time` | 基于通道周期性发送“滴答”事件。                    |\n",
    "| `time.Timer`    | `time` | 在指定时间后向通道发送单个事件。                  |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "常用 `time.Time` 方法\n",
    "\n",
    "- 获取当前时间：`time.Now()`\n",
    "- 解析字符串：`time.Parse(layout, value)` / `time.ParseInLocation(layout, value, loc)`\n",
    "- 格式化字符串：`t.Format(layout)`\n",
    "- 时间运算：`t.Add(duration)`、`t.Sub(u)`、`t.Before(u)`、`t.After(u)`\n",
    "- 提取部分字段：`t.Year()`、`t.Month()`、`t.Day()` 等"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 时间格式化与布局（Layout）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Go 采用“参考时间”布局：\n",
    "\n",
    "```shell\n",
    "Mon Jan 2 15:04:05 MST 2006\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你需要使用上述参考时间的各个数值来指定格式。例如：\n",
    "\n",
    "| 期望格式           | 布局示例                                                  |\n",
    "| ------------------ | --------------------------------------------------------- |\n",
    "| `YYYY-MM-DD`       | `2006-01-02`                                              |\n",
    "| `YYYY/MM/DD HH:mm` | `2006/01/02 15:04`                                        |\n",
    "| `HH:mm:ss`         | `15:04:05`                                                |\n",
    "| RFC3339            | `time.RFC3339` 常量（等同于 `2006-01-02T15:04:05Z07:00`） |\n",
    "| Unix 时间戳        | `t.Unix()`（秒）或 `t.UnixMilli()` 等方法                 |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "常用格式化常量\n",
    "\n",
    "- `time.RFC3339`：`2006-01-02T15:04:05Z07:00`\n",
    "- `time.RFC1123`：`Mon, 02 Jan 2006 15:04:05 MST`\n",
    "- `time.DateTime`（Go 1.20+）：`2006-01-02 15:04:05`\n",
    "- 更多可在标准库文档中找到。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 常见时间操作与格式化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当前时间（默认格式）: 2025-09-29 01:45:22.699491895 +0800 CST m=+0.000056235\n",
      "格式化（YYYY-MM-DD HH:mm:ss）: 2025-09-29 01:45:22\n",
      "RFC3339: 2025-09-29T01:45:22+08:00\n",
      "自定义带毫秒: 2025-09-29 01:45:22.699\n",
      "上海时间: 2025-09-29 01:45:22 CST\n",
      "解析后的时间: 2025-09-28 21:30:00 +0800 CST (Unix 秒: 1759066200)\n",
      "Duration 示例: 2h30m0s\n",
      "当前时间加上 Duration: 2025-09-29 04:15:22.699491895 +0800 CST m=+9000.000056235\n",
      "两个时间差值: -4h15m22.699491895s\n",
      "\n",
      "时间比较示例：\n",
      "过去时间: 2025-09-29 00:45:22\n",
      "未来时间: 2025-09-29 03:45:22\n",
      "past.Before(now)? true\n",
      "future.After(now)? true\n",
      "now.Equal(now)? true\n",
      "parsed.After(now)? false\n",
      "parsed.Before(future)? true\n",
      "\n",
      "等待 1 秒...完成！\n",
      "Ticker 开始：\n",
      "Tick: 01:45:24.200\n",
      "Tick: 01:45:24.700\n",
      "Tick: 01:45:25.200\n",
      "Ticker 结束。\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\t// 当前时间\n",
    "\tnow := time.Now()\n",
    "\tfmt.Println(\"当前时间（默认格式）:\", now)\n",
    "\n",
    "\t// 常见格式化\n",
    "\tfmt.Println(\"格式化（YYYY-MM-DD HH:mm:ss）:\", now.Format(\"2006-01-02 15:04:05\"))\n",
    "\tfmt.Println(\"RFC3339:\", now.Format(time.RFC3339))\n",
    "\tfmt.Println(\"自定义带毫秒:\", now.Format(\"2006-01-02 15:04:05.000\"))\n",
    "\n",
    "\t// 时区转换\n",
    "\tloc, err := time.LoadLocation(\"Asia/Shanghai\")\n",
    "\tif err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tfmt.Println(\"上海时间:\", now.In(loc).Format(\"2006-01-02 15:04:05 MST\"))\n",
    "\n",
    "\t// 解析时间字符串\n",
    "\tinput := \"2025-09-28 21:30:00\"\n",
    "\tparsed, err := time.ParseInLocation(\"2006-01-02 15:04:05\", input, loc)\n",
    "\tif err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tfmt.Printf(\"解析后的时间: %v (Unix 秒: %d)\\n\", parsed, parsed.Unix())\n",
    "\n",
    "\t// Duration 示例\n",
    "\tduration := 2*time.Hour + 30*time.Minute\n",
    "\tfmt.Println(\"Duration 示例:\", duration)\n",
    "\tfmt.Println(\"当前时间加上 Duration:\", now.Add(duration))\n",
    "\tfmt.Println(\"两个时间差值:\", parsed.Sub(now))\n",
    "\n",
    "\t// 时间比较示例\n",
    "\tpast := now.Add(-1 * time.Hour)\n",
    "\tfuture := now.Add(2 * time.Hour)\n",
    "\n",
    "\tfmt.Println(\"\\n时间比较示例：\")\n",
    "\tfmt.Println(\"过去时间:\", past.Format(\"2006-01-02 15:04:05\"))\n",
    "\tfmt.Println(\"未来时间:\", future.Format(\"2006-01-02 15:04:05\"))\n",
    "\tfmt.Println(\"past.Before(now)?\", past.Before(now))\n",
    "\tfmt.Println(\"future.After(now)?\", future.After(now))\n",
    "\tfmt.Println(\"now.Equal(now)?\", now.Equal(now))\n",
    "\tfmt.Println(\"parsed.After(now)?\", parsed.After(now))\n",
    "\tfmt.Println(\"parsed.Before(future)?\", parsed.Before(future))\n",
    "\n",
    "\t// Timer 示例：等待 1 秒\n",
    "\ttimer := time.NewTimer(1 * time.Second)\n",
    "\tfmt.Print(\"\\n等待 1 秒...\")\n",
    "\t<-timer.C\n",
    "\tfmt.Println(\"完成！\")\n",
    "\n",
    "\t// Ticker 示例：每 500ms 打印一次，持续 3 次后退出\n",
    "\tticker := time.NewTicker(500 * time.Millisecond)\n",
    "\tdefer ticker.Stop()\n",
    "\n",
    "\tcount := 0\n",
    "\tfmt.Println(\"Ticker 开始：\")\n",
    "\tfor t := range ticker.C {\n",
    "\t\tfmt.Println(\"Tick:\", t.Format(\"15:04:05.000\"))\n",
    "\t\tcount++\n",
    "\t\tif count == 3 {\n",
    "\t\t\tbreak\n",
    "\t\t}\n",
    "\t}\n",
    "\tfmt.Println(\"Ticker 结束。\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 39 定时执行和周期执行"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Go 标准库 `time` 包提供了轻量级的定时器与周期任务工具，常用的有：\n",
    "\n",
    "1. 一次性定时执行\n",
    "\n",
    "- **`time.NewTimer(d)`**：创建一个在 `d` 之后触发的 `Timer`，从 `Timer.C` 读取可获知触发时间。\n",
    "- **`time.AfterFunc(d, f)`**：在 `d` 之后异步调用函数 `f`（新 Goroutine 中执行）。\n",
    "- **`time.After(d)`**：返回一个 channel，`d` 时间后会收到当前时间的值，适合简单场景。\n",
    "\n",
    "2. 周期性执行\n",
    "\n",
    "- **`time.NewTicker(period)`**：按固定周期发送时间值到 `Ticker.C`，需手动 `Stop()` 停止。\n",
    "- **`time.Tick(period)`**：基于 `NewTicker(period).C` 的便捷函数，但无法关闭，不适合需要停止的场景。\n",
    "- **自定义循环 + `time.Sleep`**：实现简单周期逻辑，但对误差累积不敏感。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "常见使用注意事项\n",
    "\n",
    "- `Timer` 和 `Ticker` 底层都持有资源，用完记得调用 `Stop()`。\n",
    "- 使用 `select` 可以同时监听多个定时器或 ticker。\n",
    "- 若需精确控制结束逻辑，可以结合 `context.Context` 或自定义信号通道。\n",
    "- 长期运行的周期任务建议处理好 panic 以及延迟累积（例如使用 `time.Since` 监控）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 定时一次执行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== time.NewTimer 演示 ===\n",
      "等待 2 秒 ...\n",
      "Timer 触发，实际耗时：2.001120643s\n",
      "\n",
      "=== time.AfterFunc 演示 ===\n",
      "主协程等待 AfterFunc 完成 ...\n",
      "AfterFunc 在 2 秒后执行此函数\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"=== time.NewTimer 演示 ===\")\n",
    "\tnewTimerDemo()\n",
    "\n",
    "\tfmt.Println(\"\\n=== time.AfterFunc 演示 ===\")\n",
    "\tafterFuncDemo()\n",
    "}\n",
    "\n",
    "// 演示 time.NewTimer：等待 2 秒后继续\n",
    "func newTimerDemo() {\n",
    "\ttimer := time.NewTimer(2 * time.Second)\n",
    "\tstart := time.Now()\n",
    "\tfmt.Println(\"等待 2 秒 ...\")\n",
    "\t<-timer.C\n",
    "\tfmt.Printf(\"Timer 触发，实际耗时：%v\\n\", time.Since(start))\n",
    "}\n",
    "\n",
    "// 演示 time.AfterFunc：2 秒后执行指定函数\n",
    "func afterFuncDemo() {\n",
    "\tdone := make(chan struct{})\n",
    "\n",
    "\ttime.AfterFunc(2*time.Second, func() {\n",
    "\t\tfmt.Println(\"AfterFunc 在 2 秒后执行此函数\")\n",
    "\t\tclose(done)\n",
    "\t})\n",
    "\n",
    "\tfmt.Println(\"主协程等待 AfterFunc 完成 ...\")\n",
    "\t<-done\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 周期执行与可取消任务"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 周期执行（Ticker）===\n",
      "第 1 次 Tick：01:50:19.231\n",
      "第 2 次 Tick：01:50:19.731\n",
      "第 3 次 Tick：01:50:20.231\n",
      "第 4 次 Tick：01:50:20.731\n",
      "第 5 次 Tick：01:50:21.231\n",
      "达到 5 次，停止 ticker\n",
      "\n",
      "=== 可取消周期任务（Ticker + Context）===\n",
      "周期任务执行： 2025-09-29T01:50:22+08:00\n",
      "周期任务执行： 2025-09-29T01:50:23+08:00\n",
      "周期任务执行： 2025-09-29T01:50:24+08:00\n",
      "收到取消信号，停止周期任务\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"context\"\n",
    "\t\"fmt\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"=== 周期执行（Ticker）===\")\n",
    "\ttickerDemo()\n",
    "\n",
    "\tfmt.Println(\"\\n=== 可取消周期任务（Ticker + Context）===\")\n",
    "\tcancelableTickerDemo()\n",
    "}\n",
    "\n",
    "// 演示 time.NewTicker：每 500ms 执行一次，共执行 5 次然后停止\n",
    "func tickerDemo() {\n",
    "\tticker := time.NewTicker(500 * time.Millisecond)\n",
    "\tdefer ticker.Stop()\n",
    "\n",
    "\tcount := 0\n",
    "\tfor t := range ticker.C {\n",
    "\t\tcount++\n",
    "\t\tfmt.Printf(\"第 %d 次 Tick：%s\\n\", count, t.Format(\"15:04:05.000\"))\n",
    "\t\tif count == 5 {\n",
    "\t\t\tfmt.Println(\"达到 5 次，停止 ticker\")\n",
    "\t\t\tbreak\n",
    "\t\t}\n",
    "\t}\n",
    "}\n",
    "\n",
    "// 演示带取消能力的周期任务：每秒执行一次，3 秒后自动取消\n",
    "func cancelableTickerDemo() {\n",
    "\tctx, cancel := context.WithCancel(context.Background())\n",
    "\tdefer cancel()\n",
    "\n",
    "\tgo func() {\n",
    "\t\tticker := time.NewTicker(1 * time.Second)\n",
    "\t\tdefer ticker.Stop()\n",
    "\n",
    "\t\tfor {\n",
    "\t\t\tselect {\n",
    "\t\t\tcase t := <-ticker.C:\n",
    "\t\t\t\tfmt.Println(\"周期任务执行：\", t.Format(time.RFC3339))\n",
    "\t\t\tcase <-ctx.Done():\n",
    "\t\t\t\tfmt.Println(\"收到取消信号，停止周期任务\")\n",
    "\t\t\t\treturn\n",
    "\t\t\t}\n",
    "\t\t}\n",
    "\t}()\n",
    "\n",
    "\t// 主协程 3 秒后取消\n",
    "\ttime.Sleep(3 * time.Second)\n",
    "\tcancel()\n",
    "\n",
    "\t// 等待协程清理完\n",
    "\ttime.Sleep(500 * time.Millisecond)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 40 defer经典案例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`defer` 语句会在当前函数返回前（无论正常返回还是遇到 panic）按“后进先出”的顺序执行，非常适合做资源释放、状态回滚和收尾工作。以下是实际开发中常见的几类用法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 资源清理：文件、网络连接、锁等"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "读取失败： open example.txt: no such file or directory\n"
     ]
    }
   ],
   "source": [
    "// 打开资源后立即 defer 关闭，避免忘记释放。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"bufio\"\n",
    "\t\"fmt\"\n",
    "\t\"os\"\n",
    ")\n",
    "\n",
    "func readFirstLine(path string) (string, error) {\n",
    "\tfile, err := os.Open(path)\n",
    "\tif err != nil {\n",
    "\t\treturn \"\", err\n",
    "\t}\n",
    "\tdefer file.Close() // 确保函数返回前关闭文件\n",
    "\n",
    "\treader := bufio.NewReader(file)\n",
    "\tline, err := reader.ReadString('\\n')\n",
    "\tif err != nil {\n",
    "\t\treturn \"\", err\n",
    "\t}\n",
    "\treturn line, nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tline, err := readFirstLine(\"example.txt\")\n",
    "\tif err != nil {\n",
    "\t\tfmt.Println(\"读取失败：\", err)\n",
    "\t\treturn\n",
    "\t}\n",
    "\tfmt.Println(\"第一行：\", line)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 解锁操作：`sync.Mutex`、`sync.RWMutex`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "最终计数： 5\n"
     ]
    }
   ],
   "source": [
    "// 获取锁后立即 defer 解锁，防止提前返回时忘记解锁造成死锁。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"sync\"\n",
    ")\n",
    "\n",
    "type counter struct {\n",
    "\tmu sync.Mutex\n",
    "\tn  int\n",
    "}\n",
    "\n",
    "func (c *counter) Inc() {\n",
    "\tc.mu.Lock()\n",
    "\tdefer c.mu.Unlock()\n",
    "\n",
    "\tc.n++\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvar wg sync.WaitGroup\n",
    "\tvar c counter\n",
    "\n",
    "\tfor i := 0; i < 5; i++ {\n",
    "\t\twg.Add(1)\n",
    "\t\tgo func() {\n",
    "\t\t\tdefer wg.Done()\n",
    "\t\t\tc.Inc()\n",
    "\t\t}()\n",
    "\t}\n",
    "\n",
    "\twg.Wait()\n",
    "\tfmt.Println(\"最终计数：\", c.n)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- panic 恢复：构建健壮的库或服务"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "运行中...\n",
      "捕获 panic： something bad happened\n",
      "是否发生 panic？ true\n",
      "程序继续运行。\n"
     ]
    }
   ],
   "source": [
    "// 使用 defer + recover() 捕获 panic，避免整个程序崩溃。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func safeRun(fn func()) (panicked bool) {\n",
    "\tdefer func() {\n",
    "\t\tif r := recover(); r != nil {\n",
    "\t\t\tpanicked = true\n",
    "\t\t\tfmt.Println(\"捕获 panic：\", r)\n",
    "\t\t}\n",
    "\t}()\n",
    "\n",
    "\tfn()\n",
    "\treturn false\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"是否发生 panic？\", safeRun(func() {\n",
    "\t\tfmt.Println(\"运行中...\")\n",
    "\t\tpanic(\"something bad happened\")\n",
    "\t}))\n",
    "\n",
    "\tfmt.Println(\"程序继续运行。\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 函数退出时记录耗时、状态等"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "工作完成\n",
      "work() 耗时：200.867704ms\n"
     ]
    }
   ],
   "source": [
    "// 在函数入口处 defer 记录时间、统计信息或打日志。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func work() {\n",
    "\tstart := time.Now()\n",
    "\tdefer func() {\n",
    "\t\tfmt.Printf(\"work() 耗时：%v\\n\", time.Since(start))\n",
    "\t}()\n",
    "\n",
    "\ttime.Sleep(200 * time.Millisecond)\n",
    "\tfmt.Println(\"工作完成\")\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\twork()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 多个 defer 的执行顺序：后进先出（LIFO）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "函数主体\n",
      "defer 3\n",
      "defer 2\n",
      "defer 1\n"
     ]
    }
   ],
   "source": [
    "// 多个 defer 会倒序执行，可用于成对操作（如压栈/出栈）。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tdefer fmt.Println(\"defer 1\")\n",
    "\tdefer fmt.Println(\"defer 2\")\n",
    "\tdefer fmt.Println(\"defer 3\")\n",
    "\n",
    "\tfmt.Println(\"函数主体\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 循环中 defer 的注意事项"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "body: 0\n",
      "defer in loop: 0\n",
      "body: 1\n",
      "defer in loop: 1\n",
      "body: 2\n",
      "defer in loop: 2\n"
     ]
    }
   ],
   "source": [
    "// 循环内大量 defer 会延迟执行并消耗资源，慎用。若需要立即释放，可将操作提取到独立函数。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tfor i := 0; i < 3; i++ {\n",
    "\t\tfunc(n int) {\n",
    "\t\t\tdefer fmt.Println(\"defer in loop:\", n)\n",
    "\t\t\tfmt.Println(\"body:\", n)\n",
    "\t\t}(i)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 常见 defer 场景合集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== 文件读取示例 ==\n",
      "读取第一行: hello\n",
      "\n",
      "\n",
      "== Mutex 示例 ==\n",
      "最终计数: 5\n",
      "\n",
      "== panic 恢复示例 ==\n",
      "没有 panic\n",
      "safeRun 正常执行： false\n",
      "捕获 panic： boom\n",
      "safeRun 触发 panic： true\n",
      "\n",
      "== 耗时统计示例 ==\n",
      "进入 traceDemo\n",
      "退出 traceDemo，耗时：100.495552ms\n",
      "\n",
      "== context 收尾示例 ==\n",
      "上下文结束： context deadline exceeded\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"bufio\"\n",
    "\t\"context\"\n",
    "\t\"fmt\"\n",
    "\t\"os\"\n",
    "\t\"sync\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func readFileDemo() {\n",
    "\tfmt.Println(\"== 文件读取示例 ==\")\n",
    "\tfile, err := os.CreateTemp(\"\", \"defer_demo_*.txt\")\n",
    "\tif err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tdefer os.Remove(file.Name()) // 清理临时文件\n",
    "\tdefer file.Close()\n",
    "\n",
    "\tfile.WriteString(\"hello\\ngo\\n\")\n",
    "\n",
    "\tf, err := os.Open(file.Name())\n",
    "\tif err != nil {\n",
    "\t\tpanic(err)\n",
    "\t}\n",
    "\tdefer f.Close()\n",
    "\n",
    "\treader := bufio.NewReader(f)\n",
    "\tline, _ := reader.ReadString('\\n')\n",
    "\tfmt.Println(\"读取第一行:\", line)\n",
    "}\n",
    "\n",
    "func mutexDemo() {\n",
    "\tfmt.Println(\"\\n== Mutex 示例 ==\")\n",
    "\tvar mu sync.Mutex\n",
    "\tcounter := 0\n",
    "\n",
    "\tincrement := func() {\n",
    "\t\tmu.Lock()\n",
    "\t\tdefer mu.Unlock()\n",
    "\t\tcounter++\n",
    "\t}\n",
    "\n",
    "\tvar wg sync.WaitGroup\n",
    "\tfor i := 0; i < 5; i++ {\n",
    "\t\twg.Add(1)\n",
    "\t\tgo func() {\n",
    "\t\t\tdefer wg.Done()\n",
    "\t\t\tincrement()\n",
    "\t\t}()\n",
    "\t}\n",
    "\twg.Wait()\n",
    "\tfmt.Println(\"最终计数:\", counter)\n",
    "}\n",
    "\n",
    "func recoverDemo() {\n",
    "\tfmt.Println(\"\\n== panic 恢复示例 ==\")\n",
    "\tsafeRun := func(fn func()) (panicked bool) {\n",
    "\t\tdefer func() {\n",
    "\t\t\tif r := recover(); r != nil {\n",
    "\t\t\t\tpanicked = true\n",
    "\t\t\t\tfmt.Println(\"捕获 panic：\", r)\n",
    "\t\t\t}\n",
    "\t\t}()\n",
    "\t\tfn()\n",
    "\t\treturn false\n",
    "\t}\n",
    "\n",
    "\tfmt.Println(\"safeRun 正常执行：\", safeRun(func() {\n",
    "\t\tfmt.Println(\"没有 panic\")\n",
    "\t}))\n",
    "\n",
    "\tfmt.Println(\"safeRun 触发 panic：\", safeRun(func() {\n",
    "\t\tpanic(\"boom\")\n",
    "\t}))\n",
    "}\n",
    "\n",
    "func traceDemo() {\n",
    "\tfmt.Println(\"\\n== 耗时统计示例 ==\")\n",
    "\ttrace := func(name string) func() {\n",
    "\t\tstart := time.Now()\n",
    "\t\tfmt.Printf(\"进入 %s\\n\", name)\n",
    "\t\treturn func() {\n",
    "\t\t\tfmt.Printf(\"退出 %s，耗时：%v\\n\", name, time.Since(start))\n",
    "\t\t}\n",
    "\t}\n",
    "\n",
    "\tfunc() {\n",
    "\t\tdefer trace(\"traceDemo\")()\n",
    "\t\ttime.Sleep(100 * time.Millisecond)\n",
    "\t}()\n",
    "}\n",
    "\n",
    "func cancelContextDemo() {\n",
    "\tfmt.Println(\"\\n== context 收尾示例 ==\")\n",
    "\tctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)\n",
    "\tdefer cancel() // 确保退出时释放资源\n",
    "\n",
    "\tselect {\n",
    "\tcase <-time.After(1 * time.Second):\n",
    "\t\tfmt.Println(\"耗时操作完成\")\n",
    "\tcase <-ctx.Done():\n",
    "\t\tfmt.Println(\"上下文结束：\", ctx.Err())\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\treadFileDemo()\n",
    "\tmutexDemo()\n",
    "\trecoverDemo()\n",
    "\ttraceDemo()\n",
    "\tcancelContextDemo()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 41 panic与recover"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`panic` 是什么？\n",
    "\n",
    "- 表示程序遇到不可恢复的严重错误，立即中止当前函数的执行。\n",
    "- 会沿调用栈向上一层层传播，触发每层已注册的 `defer`。\n",
    "- 若 `panic` 未被捕获，程序将终止并输出堆栈信息。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`recover` 是什么？\n",
    "\n",
    "- 与 `panic` 配套使用，用于“拦截”正在传播的 `panic`。\n",
    "- 只能在 **被 `defer` 的函数** 中调用才有效。\n",
    "- 当捕获到 `panic` 时，返回传入 `panic` 的参数；若当前没有 `panic`，返回 `nil`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "典型用法\n",
    "\n",
    "| 场景                 | 说明                                                 |\n",
    "| -------------------- | ---------------------------------------------------- |\n",
    "| **库函数防崩溃**     | 屏蔽内部 `panic`，将错误转换为返回值。               |\n",
    "| **安全执行外部逻辑** | 执行第三方回调或插件时保护主程序。                   |\n",
    "| **统一错误处理**     | 在框架层捕获 `panic`，输出日志、清理资源后继续服务。 |\n",
    "| **测试辅助**         | 在 `testing` 中检测是否发生 `panic`。                |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用注意\n",
    "\n",
    "- `panic` 并不等同于日常的错误；只在真正“不可恢复”的场景使用。\n",
    "- `recover` 只在直接被 `defer` 调用的函数中有效，且仅对当前 Goroutine 生效。\n",
    "- 捕获 `panic` 后应妥善处理，比如记录日志、转换成 `error` 返回等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 示例 1：正常函数 ===\n",
      "没有 panic，正常结束\n",
      "返回 error: <nil>\n",
      "\n",
      "=== 示例 2：触发 panic ===\n",
      "即将触发 panic ...\n",
      "[safeExecute] 捕获 panic，已恢复\n",
      "返回 error: 数据损坏\n",
      "\n",
      "=== 示例 3：在内部处理 panic ===\n",
      "内部触发 panic\n",
      "[内部处理] 捕获 panic: 内部 panic\n",
      "主程序继续运行\n",
      "\n",
      "=== 示例 4：未捕获的 panic（程序崩溃）===\n",
      "下面这一行被注释掉以防止示例直接崩溃。\n"
     ]
    }
   ],
   "source": [
    "// 演示 panic 传播与 recover 拦截\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "// safeExecute 包装用户函数，捕获 panic 并返回 error\n",
    "func safeExecute(fn func()) (err error) {\n",
    "\tdefer func() {\n",
    "\t\tif r := recover(); r != nil {\n",
    "\t\t\t// 将 panic 信息转换为 error\n",
    "\t\t\tswitch v := r.(type) {\n",
    "\t\t\tcase error:\n",
    "\t\t\t\terr = v\n",
    "\t\t\tdefault:\n",
    "\t\t\t\terr = fmt.Errorf(\"panic: %v\", v)\n",
    "\t\t\t}\n",
    "\t\t\tfmt.Println(\"[safeExecute] 捕获 panic，已恢复\")\n",
    "\t\t}\n",
    "\t}()\n",
    "\n",
    "\tfn() // 执行用户函数，如果 panic 将被上面的 defer 捕获\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"=== 示例 1：正常函数 ===\")\n",
    "\terr := safeExecute(func() {\n",
    "\t\tfmt.Println(\"没有 panic，正常结束\")\n",
    "\t})\n",
    "\tfmt.Println(\"返回 error:\", err)\n",
    "\n",
    "\tfmt.Println(\"\\n=== 示例 2：触发 panic ===\")\n",
    "\terr = safeExecute(func() {\n",
    "\t\tfmt.Println(\"即将触发 panic ...\")\n",
    "\t\tpanic(errors.New(\"数据损坏\"))\n",
    "\t})\n",
    "\tfmt.Println(\"返回 error:\", err)\n",
    "\n",
    "\tfmt.Println(\"\\n=== 示例 3：在内部处理 panic ===\")\n",
    "\tfunc() {\n",
    "\t\tdefer func() {\n",
    "\t\t\tif r := recover(); r != nil {\n",
    "\t\t\t\tfmt.Println(\"[内部处理] 捕获 panic:\", r)\n",
    "\t\t\t}\n",
    "\t\t}()\n",
    "\t\tfmt.Println(\"内部触发 panic\")\n",
    "\t\tpanic(\"内部 panic\")\n",
    "\t}()\n",
    "\tfmt.Println(\"主程序继续运行\")\n",
    "\n",
    "\tfmt.Println(\"\\n=== 示例 4：未捕获的 panic（程序崩溃）===\")\n",
    "\tfmt.Println(\"下面这一行被注释掉以防止示例直接崩溃。\")\n",
    "\t// panic(\"未恢复的 panic 会让程序退出\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 小结与最佳实践"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 日常业务错误优先使用 `error`，`panic` 仅用于不可恢复的问题（越界、nil 指针、防御性编程等）。\n",
    "- 在框架、服务入口处使用 `recover`，确保即使出现 `panic` 也能记录日志并保持服务稳定。\n",
    "- 编写库函数时，避免将 `panic` 传播给调用方，除非文档明确说明。\n",
    "- 注意 `recover` 只捕获当前 Goroutine 的 `panic`，跨 Goroutine 需要各自处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 42 channel及引用类型的本质"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Channel 的本质\n",
    "\n",
    "- **类型化通信机制**：Channel 是带类型的通信管道，专门在 Goroutine 间安全地传递数据。\n",
    "- **引用类型**：Channel 变量内部是指向底层数据结构的引用；拷贝 channel 变量不会复制底层队列，只是新变量指向同一通道。\n",
    "- **同步语义**：未缓冲 channel 会阻塞发送或接收以实现同步；带缓冲 channel 则在缓冲区未满/非空情况下立即完成操作。\n",
    "- **双向或单向**：默认双向，可通过类型转换限制为只收（`<-chan T`）或只发（`chan<- T`）。\n",
    "- **生命周期**：通道需要显式 `close`，其后继续接收可得到零值并判定关闭，继续发送会 panic。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "引用类型的共性（Slice、Map、Channel、Function）\n",
    "\n",
    "- **底层数据共享**：变量只是一个“描述符”，拷贝变量不会复制底层数据。多个变量引用同一底层结构，修改会互相影响。\n",
    "- **零值可用**：除 slice 需要 `nil` 检查外，map、channel 的零值都不可直接使用，需要 `make` 初始化。\n",
    "- **参数传递**：按值传递描述符，但描述符内含指针指向底层数据，因此在函数内部修改会影响外部。\n",
    "- **需要谨慎拷贝**：若要独立数据，需显式深拷贝，如 `append([]T(nil), s...)` 复制 slice 内容。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Channel 行为展示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== Channel 行为演示 ==\n",
      "\n",
      "-- 未缓冲 channel --\n",
      "收到消息: hello\n",
      "收到消息: world\n",
      "\n",
      "-- 带缓冲 channel --\n",
      "读取: 1\n",
      "读取: 2\n",
      "\n",
      "-- channel 引用语义 --\n",
      "从原通道读取: 42\n",
      "channel_demo 完成\n",
      "\n",
      "-- 单向 channel --\n",
      "生产: 10\n",
      "生产: 20\n",
      "生产: 30\n",
      "消费: 10\n",
      "消费: 20\n",
      "消费: 30\n",
      "consumer 完成\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"== Channel 行为演示 ==\")\n",
    "\tunbufferedDemo()\n",
    "\tbufferedDemo()\n",
    "\treferenceSemanticsDemo()\n",
    "\tsingleDirectionDemo()\n",
    "}\n",
    "\n",
    "// 未缓冲 channel：发送与接收同步阻塞\n",
    "func unbufferedDemo() {\n",
    "\tfmt.Println(\"\\n-- 未缓冲 channel --\")\n",
    "\tch := make(chan string)\n",
    "\n",
    "\tgo func(out chan<- string) {\n",
    "\t\tout <- \"hello\"\n",
    "\t\tout <- \"world\"\n",
    "\t\tclose(out)\n",
    "\t}(ch)\n",
    "\n",
    "\tfor msg := range ch {\n",
    "\t\tfmt.Println(\"收到消息:\", msg)\n",
    "\t}\n",
    "}\n",
    "\n",
    "// 缓冲 channel：缓冲区未满时发送立即返回\n",
    "func bufferedDemo() {\n",
    "\tfmt.Println(\"\\n-- 带缓冲 channel --\")\n",
    "\tch := make(chan int, 2)\n",
    "\tch <- 1\n",
    "\tch <- 2\n",
    "\n",
    "\tfmt.Println(\"读取:\", <-ch)\n",
    "\tfmt.Println(\"读取:\", <-ch)\n",
    "}\n",
    "\n",
    "// Channel 是引用类型：多个变量引用同一底层结构\n",
    "func referenceSemanticsDemo() {\n",
    "\tfmt.Println(\"\\n-- channel 引用语义 --\")\n",
    "\tch := make(chan int, 1)\n",
    "\talias := ch\n",
    "\n",
    "\talias <- 42\n",
    "\tfmt.Println(\"从原通道读取:\", <-ch)\n",
    "\n",
    "\tclose(ch)\n",
    "\tfmt.Println(\"channel_demo 完成\")\n",
    "}\n",
    "\n",
    "// 单向 channel：限制发送端或接收端的能力\n",
    "func singleDirectionDemo() {\n",
    "\tfmt.Println(\"\\n-- 单向 channel --\")\n",
    "\tch := make(chan int, 3)\n",
    "\n",
    "\t// 生产者：只允许发送\n",
    "\tproducer := func(out chan<- int) {\n",
    "\t\tfor i := 1; i <= 3; i++ {\n",
    "\t\t\tout <- i * 10\n",
    "\t\t\tfmt.Println(\"生产:\", i*10)\n",
    "\t\t}\n",
    "\t\tclose(out)\n",
    "\t}\n",
    "\n",
    "\t// 消费者：只允许接收\n",
    "\tconsumer := func(in <-chan int) {\n",
    "\t\tfor v := range in {\n",
    "\t\t\tfmt.Println(\"消费:\", v)\n",
    "\t\t}\n",
    "\t\tfmt.Println(\"consumer 完成\")\n",
    "\t}\n",
    "\n",
    "\tgo producer(ch)\n",
    "\tconsumer(ch)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 引用类型（Slice / Map）行为展示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== 引用类型行为演示 ==\n",
      "\n",
      "-- Slice 共享 --\n",
      "base: [99 2 3]\n",
      "alias: [99 2 3]\n",
      "\n",
      "-- Slice 深拷贝 --\n",
      "base: [10 20 30]\n",
      "clone: [10 77 30]\n",
      "\n",
      "-- Map 共享 --\n",
      "base: map[go:1 rust:2]\n",
      "alias: map[go:1 rust:2]\n",
      "\n",
      "-- Channel 复用 --\n",
      "从 ch 读取: shared\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"== 引用类型行为演示 ==\")\n",
    "\tsliceAliasDemo()\n",
    "\tsliceDeepCopyDemo()\n",
    "\tmapAliasDemo()\n",
    "\tchannelAliasQuickCheck()\n",
    "}\n",
    "\n",
    "// Slice 拷贝描述符会共享底层数组\n",
    "func sliceAliasDemo() {\n",
    "\tfmt.Println(\"\\n-- Slice 共享 --\")\n",
    "\tbase := []int{1, 2, 3}\n",
    "\talias := base\n",
    "\talias[0] = 99\n",
    "\tfmt.Println(\"base:\", base)\n",
    "\tfmt.Println(\"alias:\", alias)\n",
    "}\n",
    "\n",
    "// 深拷贝 slice：复制底层数据以互不影响\n",
    "func sliceDeepCopyDemo() {\n",
    "\tfmt.Println(\"\\n-- Slice 深拷贝 --\")\n",
    "\tbase := []int{10, 20, 30}\n",
    "\tclone := append([]int(nil), base...) // 或 make+copy\n",
    "\tclone[1] = 77\n",
    "\tfmt.Println(\"base:\", base)\n",
    "\tfmt.Println(\"clone:\", clone)\n",
    "}\n",
    "\n",
    "// Map 同样是引用类型\n",
    "func mapAliasDemo() {\n",
    "\tfmt.Println(\"\\n-- Map 共享 --\")\n",
    "\tbase := map[string]int{\"go\": 1}\n",
    "\talias := base\n",
    "\talias[\"rust\"] = 2\n",
    "\tfmt.Println(\"base:\", base)\n",
    "\tfmt.Println(\"alias:\", alias)\n",
    "}\n",
    "\n",
    "// Channel 依旧是引用类型；快速验证\n",
    "func channelAliasQuickCheck() {\n",
    "\tfmt.Println(\"\\n-- Channel 复用 --\")\n",
    "\tch := make(chan string, 1)\n",
    "\talias := ch\n",
    "\talias <- \"shared\"\n",
    "\tfmt.Println(\"从 ch 读取:\", <-ch)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 43 接口有什么用怎么用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " ## 接口有什么用？\n",
    "\n",
    "| 用途                         | 说明                                                         |\n",
    "| ---------------------------- | ------------------------------------------------------------ |\n",
    "| **抽象行为，不关心实现细节** | 调用方只依赖接口定义的行为，具体实现可以随时替换。           |\n",
    "| **解耦与依赖注入**           | 让上层模块依赖接口而非具体结构体，便于组合、替换和测试。     |\n",
    "| **多态（动态派发）**         | 同一接口变量可在运行时绑定不同类型，实现“面向接口编程”。     |\n",
    "| **扩展标准库能力**           | Go 大量 API（如 `io.Reader`/`Writer`）都是接口，接口实现即可集成。 |\n",
    "| **测试与 mock**              | 利用接口替换真实依赖，实现 mock、伪造环境。                  |\n",
    "| **组合与装饰**               | 接口之间可组合，自定义类型可包装另一个接口实现功能叠加。     |\n",
    "\n",
    "> Go 接口是**隐式实现**：只要类型的方法集满足接口定义，就被视为实现该接口，无需显式声明。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 接口怎么用？常见步骤：\n",
    "\n",
    "1. **声明接口**：定义需要的行为集合。\n",
    "2. **实现接口**：任意结构体或类型提供接口所需方法即可。\n",
    "3. **依赖接口**：函数或结构体只接收接口类型，屏蔽实现细节。\n",
    "4. **运行时多态**：接口变量可在运行时指向不同的实现。\n",
    "5. **类型断言 / type switch**（可选）：从接口值还原具体类型，做额外操作。\n",
    "6. **组合接口**：通过接口嵌套扩展行为；或通过包装器（装饰器）增强功能。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 代码示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 基本的接口多态"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== 接口多态演示 ==\n",
      "[Email] -> ops@example.com: 磁盘空间 80%\n",
      "[Slack] #alerts: 请求延迟升高\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// Notifier 定义告警行为\n",
    "type Notifier interface {\n",
    "\tNotify(message string) error\n",
    "}\n",
    "\n",
    "// EmailSender 满足 Notifier 接口（值接收者）\n",
    "type EmailSender struct {\n",
    "\tAddress string\n",
    "}\n",
    "\n",
    "func (e EmailSender) Notify(message string) error {\n",
    "\tfmt.Printf(\"[Email] -> %s: %s\\n\", e.Address, message)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "// SlackBot 满足 Notifier 接口（指针接收者）\n",
    "type SlackBot struct {\n",
    "\tChannel string\n",
    "}\n",
    "\n",
    "func (s *SlackBot) Notify(message string) error {\n",
    "\tfmt.Printf(\"[Slack] #%s: %s\\n\", s.Channel, message)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "// SendAlert 通过接口发送通知\n",
    "func SendAlert(n Notifier, message string) {\n",
    "\tif n == nil {\n",
    "\t\tfmt.Println(\"未配置告警渠道\")\n",
    "\t\treturn\n",
    "\t}\n",
    "\tif err := n.Notify(message); err != nil {\n",
    "\t\tfmt.Println(\"发送失败:\", err)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"== 接口多态演示 ==\")\n",
    "\temail := EmailSender{Address: \"ops@example.com\"}\n",
    "\tslack := &SlackBot{Channel: \"alerts\"}\n",
    "\n",
    "\tSendAlert(email, \"磁盘空间 80%\")\n",
    "\tSendAlert(slack, \"请求延迟升高\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 组合多个接口实现（广播通知）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== 接口组合 ==\n",
      "[Email] -> ops@example.com: 多个渠道同时通知\n",
      "[Slack] #alerts: 多个渠道同时通知\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Notifier interface {\n",
    "\tNotify(message string) error\n",
    "}\n",
    "\n",
    "type EmailSender struct {\n",
    "\tAddress string\n",
    "}\n",
    "\n",
    "func (e EmailSender) Notify(message string) error {\n",
    "\tfmt.Printf(\"[Email] -> %s: %s\\n\", e.Address, message)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "type SlackBot struct {\n",
    "\tChannel string\n",
    "}\n",
    "\n",
    "func (s *SlackBot) Notify(message string) error {\n",
    "\tfmt.Printf(\"[Slack] #%s: %s\\n\", s.Channel, message)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "// MultiNotifier 组合多个 Notifier，实现“广播”\n",
    "type MultiNotifier []Notifier\n",
    "\n",
    "func (m MultiNotifier) Notify(message string) error {\n",
    "\tfor _, n := range m {\n",
    "\t\tif err := n.Notify(message); err != nil {\n",
    "\t\t\treturn err\n",
    "\t\t}\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func SendAlert(n Notifier, message string) {\n",
    "\tif n == nil {\n",
    "\t\tfmt.Println(\"未配置告警渠道\")\n",
    "\t\treturn\n",
    "\t}\n",
    "\tif err := n.Notify(message); err != nil {\n",
    "\t\tfmt.Println(\"发送失败:\", err)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"== 接口组合 ==\")\n",
    "\temail := EmailSender{Address: \"ops@example.com\"}\n",
    "\tslack := &SlackBot{Channel: \"alerts\"}\n",
    "\n",
    "\tmulti := MultiNotifier{email, slack}\n",
    "\tSendAlert(multi, \"多个渠道同时通知\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 接口依赖注入与解耦"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== 依赖注入与解耦 ==\n",
      "[Slack] #alerts: 应用启动成功\n",
      "[Slack] #alerts: CPU 使用率 95%\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Notifier interface {\n",
    "\tNotify(message string) error\n",
    "}\n",
    "\n",
    "type SlackBot struct {\n",
    "\tChannel string\n",
    "}\n",
    "\n",
    "func (s *SlackBot) Notify(message string) error {\n",
    "\tfmt.Printf(\"[Slack] #%s: %s\\n\", s.Channel, message)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "type App struct {\n",
    "\tNotifier Notifier\n",
    "}\n",
    "\n",
    "func (a App) Run() {\n",
    "\tSendAlert(a.Notifier, \"应用启动成功\")\n",
    "\tSendAlert(a.Notifier, \"CPU 使用率 95%\")\n",
    "}\n",
    "\n",
    "func SendAlert(n Notifier, message string) {\n",
    "\tif n == nil {\n",
    "\t\tfmt.Println(\"未配置告警渠道\")\n",
    "\t\treturn\n",
    "\t}\n",
    "\tif err := n.Notify(message); err != nil {\n",
    "\t\tfmt.Println(\"发送失败:\", err)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"== 依赖注入与解耦 ==\")\n",
    "\tslack := &SlackBot{Channel: \"alerts\"}\n",
    "\tapp := App{Notifier: slack}\n",
    "\tapp.Run()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 接口值的动态类型信息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== 接口值的动态信息 ==\n",
      "动态类型: main.EmailSender\n",
      "  邮件地址: ops@example.com\n",
      "动态类型: *main.SlackBot\n",
      "  Slack 频道: alerts\n",
      "Notifier(nil)\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Notifier interface {\n",
    "\tNotify(message string) error\n",
    "}\n",
    "\n",
    "type EmailSender struct {\n",
    "\tAddress string\n",
    "}\n",
    "\n",
    "func (e EmailSender) Notify(message string) error {\n",
    "\tfmt.Printf(\"[Email] -> %s: %s\\n\", e.Address, message)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "type SlackBot struct {\n",
    "\tChannel string\n",
    "}\n",
    "\n",
    "func (s *SlackBot) Notify(message string) error {\n",
    "\tfmt.Printf(\"[Slack] #%s: %s\\n\", s.Channel, message)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "// describeNotifier 通过类型断言获取接口背后的动态类型\n",
    "func describeNotifier(n Notifier) {\n",
    "\tif n == nil {\n",
    "\t\tfmt.Println(\"Notifier(nil)\")\n",
    "\t\treturn\n",
    "\t}\n",
    "\tfmt.Printf(\"动态类型: %T\\n\", n)\n",
    "\tswitch impl := n.(type) {\n",
    "\tcase EmailSender:\n",
    "\t\tfmt.Println(\"  邮件地址:\", impl.Address)\n",
    "\tcase *SlackBot:\n",
    "\t\tfmt.Println(\"  Slack 频道:\", impl.Channel)\n",
    "\tdefault:\n",
    "\t\tfmt.Println(\"  未知实现\")\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"== 接口值的动态信息 ==\")\n",
    "\temail := EmailSender{Address: \"ops@example.com\"}\n",
    "\tslack := &SlackBot{Channel: \"alerts\"}\n",
    "\n",
    "\tdescribeNotifier(email)\n",
    "\tdescribeNotifier(slack)\n",
    "\tdescribeNotifier(nil)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 空接口与类型断言、类型分支"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== 空接口与类型断言 ==\n",
      "断言为 float64: 3.14\n",
      "空接口保存了字符串: hello\n",
      "空接口保存了整数: 42\n",
      "空接口保存了其他类型 (struct { Name string }): {demo}\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// typeSwitch 演示空接口 + 类型分支\n",
    "func typeSwitch(v interface{}) {\n",
    "\tswitch value := v.(type) {\n",
    "\tcase string:\n",
    "\t\tfmt.Println(\"空接口保存了字符串:\", value)\n",
    "\tcase int:\n",
    "\t\tfmt.Println(\"空接口保存了整数:\", value)\n",
    "\tdefault:\n",
    "\t\tfmt.Printf(\"空接口保存了其他类型 (%T): %v\\n\", value, value)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"== 空接口与类型断言 ==\")\n",
    "\n",
    "\tvar any interface{} = 3.14\n",
    "\tif f, ok := any.(float64); ok {\n",
    "\t\tfmt.Println(\"断言为 float64:\", f)\n",
    "\t}\n",
    "\n",
    "\ttypeSwitch(\"hello\")\n",
    "\ttypeSwitch(42)\n",
    "\ttypeSwitch(struct{ Name string }{Name: \"demo\"})\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 标准库接口（io.Reader）示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "== 标准库接口示例 ==\n",
      "读取 5 字节: \"hello\"\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"io\"\n",
    "\t\"strings\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"== 标准库接口示例 ==\")\n",
    "\n",
    "\tvar reader io.Reader = strings.NewReader(\"hello interface\")\n",
    "\tbuf := make([]byte, 5)\n",
    "\n",
    "\tn, err := reader.Read(buf)\n",
    "\tif err != nil && err != io.EOF {\n",
    "\t\tfmt.Println(\"读取失败:\", err)\n",
    "\t\treturn\n",
    "\t}\n",
    "\n",
    "\tfmt.Printf(\"读取 %d 字节: %q\\n\", n, buf[:n])\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 小结与最佳实践"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. **尽量面向接口编程**：让上层逻辑依赖接口，底层实现可替换。\n",
    "2. **接口应小而精**：每个接口描述一种行为；多个行为可通过组合接口实现。\n",
    "3. **善用标准库接口**：`io.Reader/Writer`、`fmt.Stringer` 等是 Go 日常编程的基石。\n",
    "4. **测试友好**：接口便于提供 mock 或 stub，实现可测试的代码。\n",
    "5. **谨慎使用空接口**：`interface{}` 表达“任意类型”，用在需要异构数据时，但要配合类型断言或 type switch 处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 44*  面向接口编程案例--推荐系统"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 45 type定义一种新类型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 语言里，`type` 关键字有两种主要用途：\n",
    "\n",
    "\n",
    "\n",
    "1. **定义全新的类型（Type Definition）**\n",
    "\n",
    "   ```go\n",
    "   type MyInt int\n",
    "   ```\n",
    "\n",
    "   这会创建一个与底层类型（这里是 `int`）具有完全独立标识的新类型。\n",
    "    特性：\n",
    "\n",
    "   - 需要显式转换才能与底层类型或其他类型互换。\n",
    "   - 可以为其定义新的方法，从而实现自定义行为。\n",
    "\n",
    "2. **定义类型别名（Type Alias）**\n",
    "\n",
    "   ```go\n",
    "   type MyIntAlias = int\n",
    "   ```\n",
    "\n",
    "   这只是给现有类型起一个别名，不会生成新类型。\n",
    "    特性：\n",
    "\n",
    "   - 在编译器看来 `MyIntAlias` 和 `int` 完全相同。\n",
    "   - 不支持为别名单独定义方法（因为本质上仍是原类型）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 定义全新类型并关联方法\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "摄氏温度: 25.0°C\n",
      "对应的华氏温度: 77.0°F\n",
      "转成底层类型 float64: 25.0\n",
      "显式获得 Fahrenheit 类型: 77.0°F\n"
     ]
    }
   ],
   "source": [
    "// 强调 type 新类型 能创建独立类型，并且可以定义方法。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "// 定义全新类型\n",
    "type Celsius float64\n",
    "type Fahrenheit float64\n",
    "\n",
    "// 为新类型定义方法\n",
    "func (c Celsius) ToFahrenheit() Fahrenheit {\n",
    "\treturn Fahrenheit(c*9/5 + 32)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvar c Celsius = 25\n",
    "\tfmt.Printf(\"摄氏温度: %.1f°C\\n\", c)\n",
    "\tfmt.Printf(\"对应的华氏温度: %.1f°F\\n\", c.ToFahrenheit())\n",
    "\n",
    "\t// 显式转换为底层类型 float64\n",
    "\tvar base float64 = float64(c)\n",
    "\tfmt.Printf(\"转成底层类型 float64: %.1f\\n\", base)\n",
    "\n",
    "\t// 不同新类型之间也需要显式转换\n",
    "\tvar f Fahrenheit = c.ToFahrenheit()\n",
    "\tfmt.Printf(\"显式获得 Fahrenheit 类型: %.1f°F\\n\", f)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 定义类型别名并对比行为"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CelsiusAlias 值: 25.0\n",
      "直接赋给 float64: 25.0\n",
      "float64 再赋给别名: 30.0\n"
     ]
    }
   ],
   "source": [
    "// 展示 type 别名 = 原类型 与原类型没有边界，可直接互换。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "// 定义类型别名\n",
    "type CelsiusAlias = float64\n",
    "\n",
    "func main() {\n",
    "\tvar alias CelsiusAlias = 25\n",
    "\tvar plain float64 = alias // 别名与原类型完全互通\n",
    "\tfmt.Printf(\"CelsiusAlias 值: %.1f\\n\", alias)\n",
    "\tfmt.Printf(\"直接赋给 float64: %.1f\\n\", plain)\n",
    "\n",
    "\t// 验证赋值的双向兼容\n",
    "\tplain = 30\n",
    "\tvar alias2 CelsiusAlias = plain\n",
    "\tfmt.Printf(\"float64 再赋给别名: %.1f\\n\", alias2)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 46 函数类型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 中，函数是一等公民，它们有自己的 **函数类型**。理解函数类型有几个关键点：\n",
    "\n",
    "1. **函数类型由参数列表和返回值列表共同决定**\n",
    "   - 两个函数只有在参数及返回值的数量、类型都完全一致时，才属于同一函数类型。\n",
    "2. **函数类型的零值为 `nil`**\n",
    "   - 未赋值的函数变量默认为 `nil`，调用前需要确保不是 `nil`。\n",
    "3. **函数类型可以通过 `type` 声明命名**\n",
    "   - 命名之后便于复用、增强代码可读性，还能为该类型定义方法。\n",
    "4. **函数值可作为参数传递、作为返回值返回或存储在数据结构里**\n",
    "   - 这让 Go 支持常见的高阶函数模式。\n",
    "5. **仅能与 `nil` 比较**\n",
    "   - 函数值不支持比较运算，除了与 `nil` 的等值比较。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 函数类型的基本使用与 `nil` 检查"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3 + 4 = 7\n",
      "printResult -> 15\n",
      "unsetOp is nil, skip calling to avoid panic\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "type BinaryOp func(int, int) int\n",
    "\n",
    "func add(a, b int) int {\n",
    "\treturn a + b\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvar op BinaryOp\n",
    "\n",
    "\t// 给函数类型变量赋值并调用\n",
    "\top = add\n",
    "\tresult := op(3, 4)\n",
    "\tfmt.Printf(\"3 + 4 = %d\\n\", result)\n",
    "\n",
    "\t// 将函数作为参数传递\n",
    "\tprintResult(10, 5, add)\n",
    "\n",
    "\t// 演示 nil 函数值检查\n",
    "\tvar unsetOp BinaryOp\n",
    "\tif unsetOp == nil {\n",
    "\t\tfmt.Println(\"unsetOp is nil, skip calling to avoid panic\")\n",
    "\t}\n",
    "}\n",
    "\n",
    "func printResult(a, b int, op BinaryOp) {\n",
    "\tfmt.Printf(\"printResult -> %d\\n\", op(a, b))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 为命名的函数类型定义方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Apply add: 13\n",
      "Apply multiply: 42\n",
      "Using map lookup: 10 - 3 = 7\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "type BinaryOp func(int, int) int\n",
    "\n",
    "// 为函数类型定义方法\n",
    "func (op BinaryOp) Apply(a, b int) int {\n",
    "\tif op == nil {\n",
    "\t\tpanic(\"BinaryOp is nil\")\n",
    "\t}\n",
    "\treturn op(a, b)\n",
    "}\n",
    "\n",
    "func add(a, b int) int      { return a + b }\n",
    "func subtract(a, b int) int { return a - b }\n",
    "func multiply(a, b int) int { return a * b }\n",
    "\n",
    "func main() {\n",
    "\tsum := BinaryOp(add)\n",
    "\tproduct := BinaryOp(multiply)\n",
    "\n",
    "\tfmt.Printf(\"Apply add: %d\\n\", sum.Apply(6, 7))\n",
    "\tfmt.Printf(\"Apply multiply: %d\\n\", product.Apply(6, 7))\n",
    "\n",
    "\t// 将函数值存放到 map 中\n",
    "\tops := map[string]BinaryOp{\n",
    "\t\t\"+\": add,\n",
    "\t\t\"-\": subtract,\n",
    "\t\t\"*\": multiply,\n",
    "\t}\n",
    "\n",
    "\tfmt.Printf(\"Using map lookup: 10 - 3 = %d\\n\", ops[\"-\"].Apply(10, 3))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 函数类型与闭包工厂、高阶函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "original: [1 2 3 4 5]\n",
      "doubled:  [2 4 6 8 10]\n",
      "tripled:  [3 6 9 12 15]\n",
      "incremented: [2 3 4 5 6]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "type IntMapper func(int) int\n",
    "\n",
    "func mapInts(data []int, mapper IntMapper) []int {\n",
    "\tresult := make([]int, len(data))\n",
    "\tfor i, v := range data {\n",
    "\t\tresult[i] = mapper(v)\n",
    "\t}\n",
    "\treturn result\n",
    "}\n",
    "\n",
    "// 闭包工厂，根据乘数生成不同的映射函数\n",
    "func makeScaler(factor int) IntMapper {\n",
    "\treturn func(x int) int {\n",
    "\t\treturn x * factor\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tnumbers := []int{1, 2, 3, 4, 5}\n",
    "\n",
    "\tdouble := makeScaler(2)\n",
    "\ttriple := makeScaler(3)\n",
    "\n",
    "\tdoubled := mapInts(numbers, double)\n",
    "\ttripled := mapInts(numbers, triple)\n",
    "\n",
    "\tfmt.Printf(\"original: %v\\n\", numbers)\n",
    "\tfmt.Printf(\"doubled:  %v\\n\", doubled)\n",
    "\tfmt.Printf(\"tripled:  %v\\n\", tripled)\n",
    "\n",
    "\t// 直接使用字面量闭包\n",
    "\tincremented := mapInts(numbers, func(x int) int { return x + 1 })\n",
    "\tfmt.Printf(\"incremented: %v\\n\", incremented)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 与标准库类型的类比"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025/09/29 02:54:26 Listening on :8080\n",
      "signal: interrupt\n"
     ]
    }
   ],
   "source": [
    "// 访问 http://localhost:8080/ 可看到响应\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"log\"\n",
    "\t\"net/http\"\n",
    ")\n",
    "\n",
    "// h 的类型是 func(http.ResponseWriter, *http.Request)\n",
    "func main() {\n",
    "\th := func(w http.ResponseWriter, r *http.Request) {\n",
    "\t\tfmt.Fprintf(w, \"HandlerFunc demo: path=%s\\n\", r.URL.Path)\n",
    "\t}\n",
    "\n",
    "\t// http.HandlerFunc 是函数类型，定义如下（简化说明）：\n",
    "\t// type HandlerFunc func(ResponseWriter, *Request)\n",
    "\t// 它实现了 ServeHTTP 方法，因此满足 http.Handler 接口。\n",
    "\thandler := http.HandlerFunc(h)\n",
    "\n",
    "\tlog.Println(\"Listening on :8080\")\n",
    "\tif err := http.ListenAndServe(\":8080\", handler); err != nil {\n",
    "\t\tlog.Fatal(err)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 47 用函数替代接口"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当一个接口只包含单个方法时，使用函数类型往往更简洁灵活：\n",
    "\n",
    "- **减少样板**：不必为了实现接口额外声明结构体。\n",
    "- **签名即语义**：函数的参数与返回值直接表述行为。\n",
    "- **方便组合**：可直接使用匿名函数、闭包或函数工厂来动态构造行为。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 传统接口方式函数与接口的对比示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "interface: input cannot be empty\n",
      "function: input cannot be empty\n"
     ]
    }
   ],
   "source": [
    "// 对于单方法、无状态的行为，函数版本更简洁；若未来需要扩展方法或持有状态，可以回到接口/结构体实现。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    "\t\"strings\"\n",
    ")\n",
    "\n",
    "// 接口定义\n",
    "type Validator interface {\n",
    "\tValidate(string) error\n",
    "}\n",
    "\n",
    "// 结构体实现接口\n",
    "type NotEmpty struct{}\n",
    "\n",
    "func (NotEmpty) Validate(s string) error {\n",
    "\tif strings.TrimSpace(s) == \"\" {\n",
    "\t\treturn errors.New(\"input cannot be empty\")\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func runInterfaceVersion() {\n",
    "\tvar v Validator = NotEmpty{}\n",
    "\tif err := v.Validate(\"  \"); err != nil {\n",
    "\t\tfmt.Println(\"interface:\", err)\n",
    "\t}\n",
    "}\n",
    "\n",
    "// 函数类型，签名与接口方法一致\n",
    "type ValidatorFunc func(string) error\n",
    "\n",
    "func runFunctionVersion() {\n",
    "\tnotEmpty := ValidatorFunc(func(s string) error {\n",
    "\t\tif strings.TrimSpace(s) == \"\" {\n",
    "\t\t\treturn errors.New(\"input cannot be empty\")\n",
    "\t\t}\n",
    "\t\treturn nil\n",
    "\t})\n",
    "\n",
    "\tif err := notEmpty(\"  \"); err != nil {\n",
    "\t\tfmt.Println(\"function:\", err)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\trunInterfaceVersion()\n",
    "\trunFunctionVersion()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 具名函数类型替代接口"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<nil>\n",
      "validation failed: input cannot be empty\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    "\t\"strings\"\n",
    ")\n",
    "\n",
    "// 函数类型取代仅有一个方法的接口\n",
    "type Validator func(string) error\n",
    "\n",
    "func validate(input string, v Validator) error {\n",
    "\tif err := v(input); err != nil {\n",
    "\t\treturn fmt.Errorf(\"validation failed: %w\", err)\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func notEmpty(s string) error {\n",
    "\tif strings.TrimSpace(s) == \"\" {\n",
    "\t\treturn errors.New(\"input cannot be empty\")\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\terr := validate(\"hello\", notEmpty)\n",
    "\tfmt.Println(err) // <nil>\n",
    "\n",
    "\terr = validate(\"   \", notEmpty)\n",
    "\tfmt.Println(err) // validation failed: input cannot be empty\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 闭包工厂快速生成实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<nil>\n",
      "input exceeds 3 characters\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "type Validator func(string) error\n",
    "\n",
    "func maxLength(limit int) Validator {\n",
    "\treturn func(s string) error {\n",
    "\t\tif len(s) > limit {\n",
    "\t\t\treturn fmt.Errorf(\"input exceeds %d characters\", limit)\n",
    "\t\t}\n",
    "\t\treturn nil\n",
    "\t}\n",
    "}\n",
    "\n",
    "func validate(input string, v Validator) error {\n",
    "\treturn v(input)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tshortValidator := maxLength(3)\n",
    "\n",
    "\tfmt.Println(validate(\"go\", shortValidator))   // <nil>\n",
    "\tfmt.Println(validate(\"gopher\", shortValidator)) // input exceeds 3 characters\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 48 空接口本质及其使用方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 中，**空接口**写作 `interface{}`。其本质与特点：\n",
    "\n",
    "- **零方法**：空接口没有任何方法，因此所有类型都“自动”实现了空接口。\n",
    "- **泛型容器**：可用来承载任意类型的值，常用于通用容器、配置项、日志、JSON 解析等场景。\n",
    "- **动态行为**：通过类型断言（`value.(T)`）、`comma ok` 模式或 `type switch`，可在运行时恢复具体类型，实现不同的逻辑分发。\n",
    "- **注意性能与安全**：空接口带来灵活性，但需要额外的运行时检查（类型断言），同时在缺乏编译期类型约束的情况下，更易出现类型错误。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 容纳任意类型并做类型断言"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "string: \"gopher\" (len=6)\n",
      "int: 42 (double=84)\n",
      "bool: true (negated=false)\n"
     ]
    }
   ],
   "source": [
    "// 空接口作为通用容器及 type switch 的使用\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\t// []interface{} 可以容纳任意类型的元素\n",
    "\titems := []interface{}{\"gopher\", 42, true}\n",
    "\n",
    "\tfor _, item := range items {\n",
    "\t\tswitch v := item.(type) {\n",
    "\t\tcase string:\n",
    "\t\t\tfmt.Printf(\"string: %q (len=%d)\\n\", v, len(v))\n",
    "\t\tcase int:\n",
    "\t\t\tfmt.Printf(\"int: %d (double=%d)\\n\", v, v*2)\n",
    "\t\tcase bool:\n",
    "\t\t\tfmt.Printf(\"bool: %v (negated=%v)\\n\", v, !v)\n",
    "\t\tdefault:\n",
    "\t\t\tfmt.Printf(\"unknown type: %T\\n\", v)\n",
    "\t\t}\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `map[string]interface{}` 作为动态配置容器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "port: 8080\n",
      "debug mode enabled\n",
      "service name: user-api\n"
     ]
    }
   ],
   "source": [
    "// 在配置或 JSON 解码等场景下，利用 map[string]interface{} 动态存储与解析不同类型的值。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tconfig := map[string]interface{}{\n",
    "\t\t\"port\":        8080,\n",
    "\t\t\"debug\":       true,\n",
    "\t\t\"serviceName\": \"user-api\",\n",
    "\t}\n",
    "\n",
    "\t// 使用类型断言获取具体类型\n",
    "\tif port, ok := config[\"port\"].(int); ok {\n",
    "\t\tfmt.Println(\"port:\", port)\n",
    "\t}\n",
    "\n",
    "\tif debug, ok := config[\"debug\"].(bool); ok && debug {\n",
    "\t\tfmt.Println(\"debug mode enabled\")\n",
    "\t}\n",
    "\n",
    "\tif name, ok := config[\"serviceName\"].(string); ok {\n",
    "\t\tfmt.Println(\"service name:\", name)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 49 空结构体的本质及其应用场景"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **语法**：`struct{}` 表示一个没有任何字段的结构体。\n",
    "- **零大小类型（Zero-size type）**：空结构体在内存中不占空间，多个实例共享同一内存地址（但在语义上仍是不同的值）。\n",
    "- **用途聚焦于语义而非数据**：常用于表示“存在”或“完成”这样的信息，而无需存储额外数据，能有效避免浪费内存。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "典型应用场景\n",
    "\n",
    "1. **集合（Set）的实现**：利用 `map[T]struct{}` 表示元素集合，键为元素，值为空结构体，省去了布尔或占位整型的开销。\n",
    "2. **信号通知**：通过 `chan struct{}` 进行通知或同步，发送方只需发送一个空结构体表示“事件发生”或“任务完成”。\n",
    "3. **无状态方法接收者**：在实现具有行为但无状态的数据类型时，可用 `struct{}` 增加语义清晰度。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 使用 `map[T]struct{}` 构造整数集合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Contains 3? true\n",
      "Contains 5? false\n",
      "Contains 3 after removal? false\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// IntSet 使用 map[int]struct{} 来表示集合\n",
    "type IntSet map[int]struct{}\n",
    "\n",
    "func (s IntSet) Add(n int) {\n",
    "\ts[n] = struct{}{} // 空结构体不占额外内存\n",
    "}\n",
    "\n",
    "func (s IntSet) Contains(n int) bool {\n",
    "\t_, ok := s[n]\n",
    "\treturn ok\n",
    "}\n",
    "\n",
    "func (s IntSet) Remove(n int) {\n",
    "\tdelete(s, n)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\ts := IntSet{}\n",
    "\ts.Add(3)\n",
    "\ts.Add(7)\n",
    "\n",
    "\tfmt.Println(\"Contains 3?\", s.Contains(3))\n",
    "\tfmt.Println(\"Contains 5?\", s.Contains(5))\n",
    "\n",
    "\ts.Remove(3)\n",
    "\tfmt.Println(\"Contains 3 after removal?\", s.Contains(3))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 使用 `chan struct{}` 发送完成信号"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "worker: working...\n",
      "worker: done\n",
      "main: received completion signal\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func worker(done chan struct{}) {\n",
    "\tfmt.Println(\"worker: working...\")\n",
    "\ttime.Sleep(500 * time.Millisecond)\n",
    "\tfmt.Println(\"worker: done\")\n",
    "\t// 发送一个空结构体表示任务完成\n",
    "\tdone <- struct{}{}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tdone := make(chan struct{})\n",
    "\n",
    "\tgo worker(done)\n",
    "\n",
    "\t// 阻塞等待完成信号\n",
    "\t<-done\n",
    "\tfmt.Println(\"main: received completion signal\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 50 error接口及自定义Error"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `error` 接口本质\n",
    "\n",
    "- Go 内置的 `error` 接口定义非常简单：\n",
    "\n",
    "  ```go\n",
    "  type error interface {\n",
    "      Error() string\n",
    "  }\n",
    "  ```\n",
    "\n",
    "- 任何类型只要实现了 `Error() string` 方法，就被视为一个“可返回的错误”。\n",
    "- 标准库提供了便捷函数 `errors.New`、`fmt.Errorf` 等，用于快速构造实现 `error` 接口的值。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 自定义错误的经典做法\n",
    "\n",
    "1. **定义结构体，包含额外字段（如上下文信息、错误码等）**。\n",
    "2. **为该结构体实现 `Error() string` 方法**，返回描述性字符串。\n",
    "3. **根据需要配合 `errors.Is`、`errors.As` 等函数进行错误判定、解包。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 运行示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 基础错误与错误链"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "caught error: operation failed: flag was false\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "func mightFail(flag bool) error {\n",
    "\tif !flag {\n",
    "\t\treturn errors.New(\"operation failed: flag was false\")\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tif err := mightFail(false); err != nil {\n",
    "\t\tfmt.Println(\"caught error:\", err)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 自定义错误结构体 + `errors.Is` / `errors.As`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "resource not found\n",
      "app error: code=500 message=unexpected backend error\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "// 定义自定义错误类型，携带代码和消息\n",
    "type AppError struct {\n",
    "\tCode    int\n",
    "\tMessage string\n",
    "}\n",
    "\n",
    "func (e *AppError) Error() string {\n",
    "\treturn fmt.Sprintf(\"code=%d message=%s\", e.Code, e.Message)\n",
    "}\n",
    "\n",
    "// 预定义一个哨兵错误，便于 errors.Is 判定\n",
    "var ErrNotFound = errors.New(\"resource not found\")\n",
    "\n",
    "func findResource(id int) error {\n",
    "\tif id == 0 {\n",
    "\t\treturn ErrNotFound\n",
    "\t}\n",
    "\treturn &AppError{Code: 500, Message: \"unexpected backend error\"}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tif err := findResource(0); err != nil {\n",
    "\t\tswitch {\n",
    "\t\tcase errors.Is(err, ErrNotFound):\n",
    "\t\t\tfmt.Println(\"resource not found\")\n",
    "\t\tdefault:\n",
    "\t\t\tfmt.Println(\"other error:\", err)\n",
    "\t\t}\n",
    "\t}\n",
    "\n",
    "\tif err := findResource(42); err != nil {\n",
    "\t\tvar appErr *AppError\n",
    "\t\tif errors.As(err, &appErr) {\n",
    "\t\t\tfmt.Printf(\"app error: code=%d message=%s\\n\", appErr.Code, appErr.Message)\n",
    "\t\t} else {\n",
    "\t\t\tfmt.Println(\"unknown error:\", err)\n",
    "\t\t}\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 51 何时抛出error，如何处理error"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 何时返回（“抛出”）`error` \n",
    "\n",
    "在 Go 中，错误通常通过返回值来传递，而非异常机制。以下场景需考虑返回 `error`：\n",
    "\n",
    "1. **输入无效或状态不合法**：当函数检测到调用者提供的数据不满足前置条件。\n",
    "2. **外部依赖失败**：例如文件读写、网络请求、数据库操作等存在失败可能时。\n",
    "3. **内部约束被破坏**：运行中遇到不可恢复或违反业务规则的情况。\n",
    "4. **需要向上层传递失败原因**：即便当前层可以记录日志，也应返回 `error` 让调用者决定如何应对。\n",
    "\n",
    "与之相对，对于纯计算且无需外部资源、且前置条件由调用约定保证的场景，可直接返回结果而无需 `error`。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 如何处理 `error` \n",
    "\n",
    "1. **立即检查**：Go 惯例是在调用后立即用 `if err != nil { ... }` 处理，确保错误不会被遗漏。\n",
    "\n",
    "2. **按需包装或转换**：使用 `fmt.Errorf(\"context: %w\", err)` 添加上下文；使用 `errors.Is` / `errors.As` 区分不同类型。\n",
    "\n",
    "3. **根据语义决定策略**：\n",
    "  - 简单失败：记录日志并返回/中断。\n",
    "  - 可恢复错误：重试、回退或提供默认值。\n",
    "  - 致命状态：可能需要触发 `panic`（如检查逻辑中的不变式被破坏）。\n",
    "\n",
    "4. **保证资源清理**：使用 `defer` 关闭文件、Conn 等，即便返回错误也确保资源释放。\n",
    "\n",
    "5. **面向调用者的友好信息**：向上返回的错误应携带足够语义，使调用者能够决策下一步行为。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 运行示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 输入校验失败返回错误"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input=\"Alice\" normalized=\"alice\"\n",
      "input=\"   \" error=username cannot be empty\n",
      "input=\"ThisIsWayTooLongForUsername\" error=username \"ThisIsWayTooLongForUsername\" is too long\n"
     ]
    }
   ],
   "source": [
    "// 何时返回错误：当输入不符合约束时立即返回，并提供说明。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    "\t\"strings\"\n",
    ")\n",
    "\n",
    "func normalizeUsername(raw string) (string, error) {\n",
    "\ttrimmed := strings.TrimSpace(raw)\n",
    "\tif trimmed == \"\" {\n",
    "\t\treturn \"\", errors.New(\"username cannot be empty\")\n",
    "\t}\n",
    "\tif len(trimmed) > 16 {\n",
    "\t\treturn \"\", fmt.Errorf(\"username %q is too long\", trimmed)\n",
    "\t}\n",
    "\treturn strings.ToLower(trimmed), nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\ttests := []string{\"Alice\", \"   \", \"ThisIsWayTooLongForUsername\"}\n",
    "\tfor _, name := range tests {\n",
    "\t\tnormalized, err := normalizeUsername(name)\n",
    "\t\tif err != nil {\n",
    "\t\t\tfmt.Printf(\"input=%q error=%v\\n\", name, err)\n",
    "\t\t\tcontinue\n",
    "\t\t}\n",
    "\t\tfmt.Printf(\"input=%q normalized=%q\\n\", name, normalized)\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 处理外部资源错误并加上上下文"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "copy failed: open source: open input.txt: no such file or directory\n"
     ]
    }
   ],
   "source": [
    "// 如何处理错误：对外部资源操作进行上下文包装，并在错误发生时负责清理资源。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"io\"\n",
    "\t\"os\"\n",
    ")\n",
    "\n",
    "func copyFile(src, dst string) error {\n",
    "\tsource, err := os.Open(src)\n",
    "\tif err != nil {\n",
    "\t\treturn fmt.Errorf(\"open source: %w\", err)\n",
    "\t}\n",
    "\tdefer source.Close()\n",
    "\n",
    "\ttarget, err := os.Create(dst)\n",
    "\tif err != nil {\n",
    "\t\treturn fmt.Errorf(\"create destination: %w\", err)\n",
    "\t}\n",
    "\tdefer func() {\n",
    "\t\t// 如果写入过程出错，确保文件被删除，避免残留\n",
    "\t\tif err != nil {\n",
    "\t\t\ttarget.Close()\n",
    "\t\t\tos.Remove(dst)\n",
    "\t\t}\n",
    "\t}()\n",
    "\n",
    "\tif _, err = io.Copy(target, source); err != nil {\n",
    "\t\treturn fmt.Errorf(\"copy data: %w\", err)\n",
    "\t}\n",
    "\n",
    "\tif err = target.Close(); err != nil {\n",
    "\t\treturn fmt.Errorf(\"close destination: %w\", err)\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tif err := copyFile(\"input.txt\", \"output.txt\"); err != nil {\n",
    "\t\tfmt.Println(\"copy failed:\", err)\n",
    "\t\treturn\n",
    "\t}\n",
    "\tfmt.Println(\"copy succeeded\")\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 模拟服务降级删除目录"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "遇到权限问题，执行降级方案\n",
      "fallback plan: mark dir for async deletion: /a/b/c\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "// 模拟的哨兵错误：没有权限删除目录\n",
    "var ErrNotPermitted = errors.New(\"delete not permitted\")\n",
    "\n",
    "func deleteDir(dir string) error {\n",
    "\t// 假设业务逻辑：某些重要目录不允许直接删除\n",
    "\tif dir == \"/a/b/c\" {\n",
    "\t\treturn fmt.Errorf(\"wrap: %w\", ErrNotPermitted) // 包装后返回\n",
    "\t}\n",
    "\tfmt.Println(\"delete success:\", dir)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "// fallbackDelete 模拟降级方案，例如只清空目录或标记待删除\n",
    "func fallbackDelete(dir string) error {\n",
    "\tfmt.Println(\"fallback plan: mark dir for async deletion:\", dir)\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func scheduleTask() {\n",
    "\tdir := \"/a/b/c\"\n",
    "\n",
    "\tif err := tryDeleteWithFallback(dir); err != nil {\n",
    "\t\tfmt.Printf(\"最终仍失败，dir=%s err=%v\\n\", dir, err)\n",
    "\t}\n",
    "}\n",
    "\n",
    "// tryDeleteWithFallback 尝试删除，失败时择机降级\n",
    "func tryDeleteWithFallback(dir string) error {\n",
    "\tif err := deleteDir(dir); err != nil {\n",
    "\t\tif errors.Is(err, ErrNotPermitted) {\n",
    "\t\t\tfmt.Println(\"遇到权限问题，执行降级方案\")\n",
    "\t\t\tif fallbackErr := fallbackDelete(dir); fallbackErr != nil {\n",
    "\t\t\t\treturn fmt.Errorf(\"fallback 失败: %w\", fallbackErr)\n",
    "\t\t\t}\n",
    "\t\t\treturn nil\n",
    "\t\t}\n",
    "\t\treturn fmt.Errorf(\"deleteDir 失败: %w\", err)\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tscheduleTask()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 52 error的追踪，errors.Is和errors.As"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "错误追踪（Error Tracing）\n",
    "\n",
    "- **核心思想**：在向上返回错误时不断添加上下文信息，以便追踪错误发生的位置和原因。\n",
    "- **实现手段**：\n",
    "  - `fmt.Errorf(\"context: %w\", err)`：用 `%w` 包装原始错误，形成错误链。\n",
    "  - `errors.Unwrap(err)`、`errors.Join(...)`：对链进行解包或合并。\n",
    "  - 打印错误时既能看到最新上下文，又保留原始错误供后续判断。\n",
    "- **惯例**：每次向上返回错误前加上关键上下文描述，例如函数名、业务场景或参数值。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`errors.Is`\n",
    "\n",
    "- **用途**：判断错误链上是否存在目标错误（哨兵错误、预定义错误）。\n",
    "- **优势**：不仅匹配同一实例，还能识别被多层包装后的相同语义错误。\n",
    "- **典型场景**：检测是否是“文件不存在”“权限不足”等特定错误类型，决定重试、降级或告警策略。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`errors.As`\n",
    "\n",
    "- **用途**：将错误链上匹配的某种类型的错误赋值给目标变量，以便访问其字段（例如状态码、附加信息）。\n",
    "- **典型场景**：自定义错误类型携带上下文（比如 `AppError`、`HTTPError`），调用者使用 `errors.As` 提取并做更细粒度处理。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 错误追踪与链路打印"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "top-level error: fetch user profile (id=42): exec query \"SELECT * FROM users WHERE id=?\": database timeout\n",
      "cause #1: exec query \"SELECT * FROM users WHERE id=?\": database timeout\n",
      "cause #2: database timeout\n"
     ]
    }
   ],
   "source": [
    "// 通过 %w 构建的错误链保留了完整上下文，可逐层追踪原因。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "var ErrDBTimeout = errors.New(\"database timeout\")\n",
    "\n",
    "func queryDatabase(query string) error {\n",
    "\treturn fmt.Errorf(\"exec query %q: %w\", query, ErrDBTimeout)\n",
    "}\n",
    "\n",
    "func fetchUserProfile(userID int) error {\n",
    "\tif err := queryDatabase(\"SELECT * FROM users WHERE id=?\"); err != nil {\n",
    "\t\treturn fmt.Errorf(\"fetch user profile (id=%d): %w\", userID, err)\n",
    "\t}\n",
    "\treturn nil\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\terr := fetchUserProfile(42)\n",
    "\tif err != nil {\n",
    "\t\tfmt.Println(\"top-level error:\", err)\n",
    "\n",
    "\t\t// 手动沿链打印每一层上下文\n",
    "\t\tfor i, unwrap := 1, errors.Unwrap(err); unwrap != nil; i, unwrap = i+1, errors.Unwrap(unwrap) {\n",
    "\t\t\tfmt.Printf(\"cause #%d: %v\\n\", i, unwrap)\n",
    "\t\t}\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- `errors.Is` / `errors.As` 的使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/admin → forbidden, trigger fallback or notify user.\n",
      "/metrics → upstream error (code=502, msg=bad gateway)\n"
     ]
    }
   ],
   "source": [
    "// /admin 触发被包装的 ErrForbidden，errors.Is 能正确识别。\n",
    "// /metrics 返回自定义 APIError，errors.As 将其解包到结构体指针，便于读取错误码等信息。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"errors\"\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "var ErrForbidden = errors.New(\"forbidden\")\n",
    "\n",
    "type APIError struct {\n",
    "\tCode    int\n",
    "\tMessage string\n",
    "}\n",
    "\n",
    "func (e *APIError) Error() string {\n",
    "\treturn fmt.Sprintf(\"api error code=%d message=%s\", e.Code, e.Message)\n",
    "}\n",
    "\n",
    "func callAPI(endpoint string) error {\n",
    "\tswitch endpoint {\n",
    "\tcase \"/admin\":\n",
    "\t\treturn fmt.Errorf(\"wrap: %w\", ErrForbidden)\n",
    "\tdefault:\n",
    "\t\treturn &APIError{Code: 502, Message: \"bad gateway\"}\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\ttests := []string{\"/admin\", \"/metrics\"}\n",
    "\n",
    "\tfor _, endpoint := range tests {\n",
    "\t\terr := callAPI(endpoint)\n",
    "\t\tif err == nil {\n",
    "\t\t\tfmt.Println(endpoint, \"ok\")\n",
    "\t\t\tcontinue\n",
    "\t\t}\n",
    "\n",
    "\t\tswitch {\n",
    "\t\tcase errors.Is(err, ErrForbidden):\n",
    "\t\t\tfmt.Println(endpoint, \"→ forbidden, trigger fallback or notify user.\")\n",
    "\t\tdefault:\n",
    "\t\t\tvar apiErr *APIError\n",
    "\t\t\tif errors.As(err, &apiErr) {\n",
    "\t\t\t\tfmt.Printf(\"%s → upstream error (code=%d, msg=%s)\\n\", endpoint, apiErr.Code, apiErr.Message)\n",
    "\t\t\t} else {\n",
    "\t\t\t\tfmt.Println(endpoint, \"→ unknown error:\", err)\n",
    "\t\t\t}\n",
    "\t\t}\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 53* 综合练习：PKCS7数据填充算法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 54 结构体方法接收值和接收指针的区别"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "| 方面             | 值接收者 (`func (t T) Method()`)                      | 指针接收者 (`func (t *T) Method()`)                          |\n",
    "| ---------------- | ----------------------------------------------------- | ------------------------------------------------------------ |\n",
    "| **是否修改原值** | 方法内部对字段的修改只作用于副本，原实例不变。        | 可以直接修改原实例的字段。                                   |\n",
    "| **内存开销**     | 调用时会复制整个结构体，适合体积小或不可变对象。      | 仅传递指针，适合结构体较大或需避免频繁拷贝。                 |\n",
    "| **方法集**       | 定义在 `T` 上；既可通过值也可通过指针调用。           | 定义在 `*T` 上；仅指针和能取地址的变量可以调用。值调用时编译器会自动取地址（若可寻址）。 |\n",
    "| **接口实现**     | 若接口的方法签名使用值接收者，则 `T` 和 `*T` 都实现。 | 若接口的方法签名使用指针接收者，则只有 `*T` 实现，`T` 不满足。 |\n",
    "| **并发安全**     | 拷贝出来的值不共享状态，适合读方法或小型不可变方法。  | 共享同一实例，需要额外的并发控制。                           |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**实践经验**：\n",
    "- 需要改变接收者状态、避免大对象复制、需与 `sync.Mutex` 等字段一起使用时，用指针接收者。\n",
    "- 逻辑上只读、结构体很小、想让方法适用于值类型（如 map key）时，用值接收者。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 值接收与指针接收的差异"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始: 5\n",
      "[IncValue] 内部: requests = 6\n",
      "值接收者调用后: 5\n",
      "[IncPointer] 内部: requests = 6\n",
      "指针接收者调用后: 6\n",
      "Reset 后: 0\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Counter struct {\n",
    "\tName string\n",
    "\tN    int\n",
    "}\n",
    "\n",
    "// 值接收者：对 c 的修改只作用于副本\n",
    "func (c Counter) IncValue() {\n",
    "\tc.N++\n",
    "\tfmt.Printf(\"[IncValue] 内部: %s = %d\\n\", c.Name, c.N)\n",
    "}\n",
    "\n",
    "// 指针接收者：直接修改原始实例\n",
    "func (c *Counter) IncPointer() {\n",
    "\tc.N++\n",
    "\tfmt.Printf(\"[IncPointer] 内部: %s = %d\\n\", c.Name, c.N)\n",
    "}\n",
    "\n",
    "// 指针接收者方法，重置计数器\n",
    "func (c *Counter) Reset() {\n",
    "\tc.N = 0\n",
    "}\n",
    "\n",
    "type Resetter interface {\n",
    "\tReset()\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tc := Counter{Name: \"requests\", N: 5}\n",
    "\n",
    "\tfmt.Println(\"初始:\", c.N)\n",
    "\n",
    "\t// 调用值接收者方法\n",
    "\tc.IncValue()\n",
    "\tfmt.Println(\"值接收者调用后:\", c.N) // 仍为 5\n",
    "\n",
    "\t// 调用指针接收者方法（编译器自动取地址）\n",
    "\tc.IncPointer()\n",
    "\tfmt.Println(\"指针接收者调用后:\", c.N) // 变为 6\n",
    "\n",
    "\t// 通过指针类型满足接口\n",
    "\tvar r Resetter = &c\n",
    "\tr.Reset()\n",
    "\tfmt.Println(\"Reset 后:\", c.N)\n",
    "\n",
    "\t// 注意：以下语句若取消注释会编译失败，因为 Counter（值）不实现 Resetter\n",
    "\t// var r2 Resetter = c // 编译错误：Counter 没有值接收者 Reset 方法\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<style>\n",
       ".gonb-err-location {\n",
       "\tbackground: var(--jp-err-color2);  \n",
       "\tborder-radius: 3px;\n",
       "\tborder-style: dotted;\n",
       "\tborder-width: 1px;\n",
       "\tborder-color: var(--jp-border-color2);\n",
       "}\n",
       ".gonb-err-location:hover {\n",
       "\tborder-width: 2px;\n",
       "\tborder-style: solid;\n",
       "\tborder-color: var(--jp-border-color2);\n",
       "}\n",
       ".gonb-err-context {\n",
       "\tdisplay: none;\n",
       "}\n",
       ".gonb-err-location:hover + .gonb-err-context {\n",
       "\tbackground: var(--jp-dialog-background);  \n",
       "\tborder-radius: 3px;\n",
       "\tborder-style: solid;\n",
       "\tborder-width: 1px;\n",
       "\tborder-color: var(--jp-border-color2);\n",
       "\tdisplay: block;\n",
       "\twhite-space: pre;\n",
       "\tfont-family: monospace;\n",
       "}\n",
       ".gonb-err-line {\n",
       "\tborder-radius: 3px;\n",
       "\tborder-style: dotted;\n",
       "\tborder-width: 1px;\t\n",
       "\tborder-color: var(--jp-border-color2);\n",
       "\tbackground-color: var(--jp-rendermime-err-background);\n",
       "\tfont-weight: bold;\n",
       "}\n",
       ".gonb-cell-line-info {\n",
       "\tbackground: var(--jp-layout-color2);\n",
       "\tcolor: #999;\n",
       "\tmargin: 0.1em;\n",
       "\tborder: 1px solid var(--jp-border-color1);\n",
       "\tpadding-left: 0.2em;\n",
       "\tpadding-right: 0.2em;\n",
       "}\n",
       "</style>\n",
       "<div class=\"lm-Widget p-Widget lm-Panel p-Panel jp-OutputArea-child\">\n",
       "<div class=\"lm-Widget p-Widget jp-RenderedText jp-mod-trusted jp-OutputArea-output\" data-mime-type=\"application/vnd.jupyter.stderr\" style=\"font-family: monospace;\">\n",
       "\n",
       "\n",
       "<span style=\"white-space: pre;\"> # gonb_4b293135</span>\n",
       "\n",
       "<br/>\n",
       "\n",
       "<span class=\"gonb-cell-line-info\">Cell[17]: Line 42</span>\n",
       "<span class=\"gonb-err-location\">./main.go:206:6: </span> declared and not used: r2\n",
       "<div class=\"gonb-err-context\">\n",
       "\tfmt.Println(&#34;Reset 后:&#34;, c.N)\n",
       "\n",
       "\t// 注意：以下语句若取消注释会编译失败，因为 Counter（值）不实现 Resetter\n",
       "<div class=\"gonb-err-line\">\tvar r2 Resetter = c // 编译错误：Counter 没有值接收者 Reset 方法\n",
       "</div>}\n",
       "\n",
       "\n",
       "</div>\n",
       "\n",
       "<br/>\n",
       "\n",
       "<span class=\"gonb-cell-line-info\">Cell[17]: Line 42</span>\n",
       "<span class=\"gonb-err-location\">./main.go:206:20: </span> cannot use c (variable of struct type Counter) as Resetter value in variable declaration: Counter does not implement Resetter (method Reset has pointer receiver)\n",
       "<div class=\"gonb-err-context\">\n",
       "\tfmt.Println(&#34;Reset 后:&#34;, c.N)\n",
       "\n",
       "\t// 注意：以下语句若取消注释会编译失败，因为 Counter（值）不实现 Resetter\n",
       "<div class=\"gonb-err-line\">\tvar r2 Resetter = c // 编译错误：Counter 没有值接收者 Reset 方法\n",
       "</div>}\n",
       "\n",
       "\n",
       "</div>\n",
       "\n",
       "<br/>\n",
       "\n",
       "\n",
       "<span style=\"white-space: pre;\"> </span>\n",
       "\n",
       "<br/>\n",
       "\n",
       "</div>\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "ename": "ERROR",
     "evalue": "failed to run \"/usr/local/go/bin/go build -o /tmp/gonb_4b293135/gonb_4b293135\": exit status 1",
     "output_type": "error",
     "traceback": [
      "failed to run \"/usr/local/go/bin/go build -o /tmp/gonb_4b293135/gonb_4b293135\": exit status 1"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Counter struct {\n",
    "\tName string\n",
    "\tN    int\n",
    "}\n",
    "\n",
    "// 值接收者：对 c 的修改只作用于副本\n",
    "func (c Counter) IncValue() {\n",
    "\tc.N++\n",
    "\tfmt.Printf(\"[IncValue] 内部: %s = %d\\n\", c.Name, c.N)\n",
    "}\n",
    "\n",
    "// 指针接收者：直接修改原始实例\n",
    "func (c *Counter) IncPointer() {\n",
    "\tc.N++\n",
    "\tfmt.Printf(\"[IncPointer] 内部: %s = %d\\n\", c.Name, c.N)\n",
    "}\n",
    "\n",
    "// 指针接收者方法，重置计数器\n",
    "func (c *Counter) Reset() {\n",
    "\tc.N = 0\n",
    "}\n",
    "\n",
    "type Resetter interface {\n",
    "\tReset()\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tc := Counter{Name: \"requests\", N: 5}\n",
    "\n",
    "\tfmt.Println(\"初始:\", c.N)\n",
    "\n",
    "\t// 通过指针类型满足接口\n",
    "\tvar r Resetter = &c\n",
    "\tr.Reset()\n",
    "\tfmt.Println(\"Reset 后:\", c.N)\n",
    "\n",
    "\t// 注意：以下语句若取消注释会编译失败，因为 Counter（值）不实现 Resetter\n",
    "\tvar r2 Resetter = c // 编译错误：Counter 没有值接收者 Reset 方法\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 55 各种数据类型的零值，以及返回零值的设计原则"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Go 各类数据类型的零值一览\n",
    "\n",
    "| 类别   | 典型类型                  | 零值                   |\n",
    "| ------ | ------------------------- | ---------------------- |\n",
    "| 布尔   | `bool`                    | `false`                |\n",
    "| 整型   | `int`, `int8`, `uint`, …  | `0`                    |\n",
    "| 浮点   | `float32`, `float64`      | `0`                    |\n",
    "| 复数   | `complex64`, `complex128` | `0` (`0 + 0i`)         |\n",
    "| 字符串 | `string`                  | `\"\"`（空字符串）       |\n",
    "| 指针   | `*T`                      | `nil`                  |\n",
    "| 切片   | `[]T`                     | `nil`                  |\n",
    "| 映射   | `map[K]V`                 | `nil`                  |\n",
    "| 通道   | `chan T`                  | `nil`                  |\n",
    "| 接口   | `interface{}`、`error` 等 | `nil`                  |\n",
    "| 函数   | `func`                    | `nil`                  |\n",
    "| 数组   | `[N]T`                    | 元素全为 `T` 的零值    |\n",
    "| 结构体 | `struct{...}`             | 每个字段为其类型的零值 |\n",
    "\n",
    "> **官方描述与设计理念**\n",
    ">\n",
    "> - Go 语言规范（The Go Programming Language Specification）指出：**“没有显式初始化的变量会被赋予零值 (The zero value is...)”**。\n",
    "> - 《Effective Go》强调：Go 语言力求 **“让零值即可直接使用（make the zero value useful）”**。因此，像 `bytes.Buffer`、`sync.Mutex`、`os.File` 等类型即使只声明未初始化，也能安全使用或调用其方法。\n",
    "\n",
    "这种设计理念的好处是减少显式初始化和 `nil` 检查，让代码更简洁、安全。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 各种基本类型和复合类型即使只声明、不初始化，也能立即打印或使用其零值，不会产生未定义行为。\n",
    "- **接口断言失败**会返回目标类型的零值及 `false`；\n",
    "- **从未初始化的 `map` 读取**会得到值类型的零值，同时可用 `exists` 辨别是否存在；\n",
    "- **关闭且被读取的通道**返回元素类型的零值和 `false`；\n",
    "- 提醒 `map` 必须初始化后才能写入，否则触发 `panic`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "bool: false\n",
      "int: 0\n",
      "float64: 0\n",
      "string: \"\"\n",
      "*Point: <nil>\n",
      "array: [0 0 0]\n",
      "slice == nil? true\n",
      "map == nil? true\n",
      "chan == nil? true\n",
      "interface == nil? true\n",
      "func == nil? true\n",
      "struct Point: {X:0 Y:0}\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// User 结构体的字段也会自动被赋予各自类型的零值\n",
    "type User struct {\n",
    "\tGender bool\n",
    "\tAge    int\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\t// 声明各类变量但不显式初始化，它们都会获得对应的零值\n",
    "\tvar (\n",
    "\t\t\t\tb   bool          // bool 类型的零值是 false\n",
    "        i   int           // 各种整型（含 int、int64、rune 等）的零值是 0\n",
    "        f   float64       // 浮点型（float32、float64）的零值是 0\n",
    "        s   string        // 字符串的零值是空串 \"\"\n",
    "        p   *int          // 指针类型（*T）的零值是 nil\n",
    "        arr [3]int        // 数组的零值是所有元素为元素类型的零值，这里是 [0 0 0]\n",
    "        sl  []string      // 切片的零值是 nil（长度和容量都为 0）\n",
    "        mp  map[int]bool  // map 的零值是 nil，未分配底层存储\n",
    "        ch  chan int      // channel 的零值是 nil，尚未初始化的通道\n",
    "        err error         // 接口类型（包括 error）的零值是 nil\n",
    "        u   User          // 结构体零值中，每个字段都是其类型的零值\n",
    "\t)\n",
    "\n",
    "\tfmt.Printf(\"bool: %v\\n\", b)\n",
    "\tfmt.Printf(\"int: %v\\n\", i)\n",
    "\tfmt.Printf(\"float64: %v\\n\", f)\n",
    "\tfmt.Printf(\"string: %q\\n\", s)\n",
    "\tfmt.Printf(\"*int: %v\\n\", p)\n",
    "\tfmt.Printf(\"array: %v\\n\", arr)\n",
    "\tfmt.Printf(\"slice == nil? %v\\n\", sl == nil)\n",
    "\tfmt.Printf(\"map == nil? %v\\n\", mp == nil)\n",
    "\tfmt.Printf(\"chan == nil? %v\\n\", ch == nil)\n",
    "\tfmt.Printf(\"error == nil? %v\\n\", err == nil)\n",
    "\tfmt.Printf(\"struct User: %+v\\n\", u) // %+v 会带字段名输出结构体\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 56 nil非nil"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- **语言规范** 将 `nil` 预先声明为指针、函数、切片、映射、通道、接口等引用类型的零值，体现了 Go 对零值语义的严格定义。\n",
    "- **Effective Go** 再强调 “零值即有效” 的理念：这类引用类型只要声明，即可安全判空和比较，无需额外初始化。\n",
    "- `nil` 本身不带类型；只有当它与具体类型（如 `*T`、`map[K]V`、`chan T` 等）结合时，编译器才会为其推断出确切的静态类型。正因如此，虽然六种常见 `nil` 的字面写法相同，但类型信息各异，彼此并不等价。\n",
    "- nil 指针保留了完整的类型信息，可在不解引用的前提下调用其方法；而接口值要同时满足 “动态类型为 nil、动态值为 nil” 才算真正的 nil 接口。只要接口绑定了具体类型（哪怕底层值仍是 nil），就再也不等于 nil，这也导致了 “函数返回 nil 指针赋给接口却不为 nil” 的经典陷阱。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 零值即有效（Zero Values are Useful）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== Zero Values ===\n",
      "sl == nil: true\n",
      "mp == nil: true\n",
      "ch == nil: true\n",
      "fn == nil: true\n",
      "err == nil: true\n",
      "append 后的切片: [42]\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tvar (\n",
    "\t\tsl []int\n",
    "\t\tmp map[string]int\n",
    "\t\tch chan struct{}\n",
    "\t\tfn func()\n",
    "\t\terr error\n",
    "\t)\n",
    "\n",
    "\tfmt.Println(\"=== Zero Values ===\")\n",
    "\tfmt.Printf(\"sl == nil: %v\\n\", sl == nil)\n",
    "\tfmt.Printf(\"mp == nil: %v\\n\", mp == nil)\n",
    "\tfmt.Printf(\"ch == nil: %v\\n\", ch == nil)\n",
    "\tfmt.Printf(\"fn == nil: %v\\n\", fn == nil)\n",
    "\tfmt.Printf(\"err == nil: %v\\n\", err == nil)\n",
    "\n",
    "\t// nil 切片可直接 append；nil map、channel、func 必须初始化后才能使用\n",
    "\tsl = append(sl, 42)\n",
    "\tfmt.Printf(\"append 后的切片: %v\\n\", sl)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- nil 指针携带类型信息，可安全调用方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ptr == nil: true\n",
      "ptr.Error(): \"TP\"\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type TP int\n",
    "\n",
    "func (tp *TP) Error() string { return \"TP\" }\n",
    "\n",
    "func main() {\n",
    "\tvar ptr *TP // nil 指针，但类型是 *TP\n",
    "\n",
    "\tfmt.Printf(\"ptr == nil: %v\\n\", ptr == nil)\n",
    "\tfmt.Printf(\"ptr.Error(): %q\\n\", ptr.Error())\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 接口 = 动态类型 + 动态值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始 err == nil: true\n",
      "赋值后 err == nil: false\n",
      "err 的动态类型: *main.TP\n",
      "err.Error(): \"TP\"\n",
      "空接口绑定 *TP 后 any == nil: false，类型: *main.TP\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type TP int\n",
    "\n",
    "func (tp *TP) Error() string { return \"TP\" }\n",
    "\n",
    "func main() {\n",
    "\tvar (\n",
    "\t\tptr *TP          // nil 指针\n",
    "\t\terr error        // nil 接口\n",
    "\t\tany interface{}  // nil 空接口\n",
    "\t)\n",
    "\n",
    "\tfmt.Printf(\"初始 err == nil: %v\\n\", err == nil)\n",
    "\n",
    "\terr = ptr // 接口绑定 *TP 类型\n",
    "\tfmt.Printf(\"赋值后 err == nil: %v\\n\", err == nil)\n",
    "\tfmt.Printf(\"err 的动态类型: %T\\n\", err)\n",
    "\tfmt.Printf(\"err.Error(): %q\\n\", err.Error())\n",
    "\n",
    "\tany = ptr\n",
    "\tfmt.Printf(\"空接口绑定 *TP 后 any == nil: %v，类型: %T\\n\", any == nil, any)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 函数返回 nil 指针的接口陷阱"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始 err == nil: true\n",
      "NewTP() 之后 err == nil: false\n",
      "err.Error(): \"TP\"\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type TP int\n",
    "\n",
    "func (tp *TP) Error() string { return \"TP\" }\n",
    "\n",
    "func NewTP() *TP { return nil } // 返回 nil 指针\n",
    "\n",
    "func main() {\n",
    "\tvar err error\n",
    "\n",
    "\tfmt.Printf(\"初始 err == nil: %v\\n\", err == nil)\n",
    "\n",
    "\terr = NewTP() // 函数返回 nil 指针，赋给接口\n",
    "\tfmt.Printf(\"NewTP() 之后 err == nil: %v\\n\", err == nil)\n",
    "\n",
    "\tif err != nil {\n",
    "\t\tfmt.Printf(\"err.Error(): %q\\n\", err.Error())\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Go 里，一个接口值由 **动态类型（dynamic type）** 和 **动态值（dynamic value）** 组成。只有当二者同时为 `nil` 时，接口值才被视为真正的 `nil interface`。这也意味着：\n",
    "\n",
    "> **一旦接口绑定了某个具体类型，即使该类型的值是 `nil`，接口本身也不再等于 `nil`**\n",
    "\n",
    "假设有一个构造函数或工厂函数：\n",
    "\n",
    "```go\n",
    "func NewTP() *TP { return nil }\n",
    "```\n",
    "\n",
    "- 它返回一个 `*TP` 类型的指针。\n",
    "- 指针的零值就是 `nil`，所以这个函数确实返回了 “nil 指针”。\n",
    "\n",
    "如果我们把这个返回值赋给接口变量：\n",
    "\n",
    "```go\n",
    "var err error\n",
    "err = NewTP()\n",
    "```\n",
    "\n",
    "此时的 `err`：\n",
    "\n",
    "- **动态类型**：`*TP`\n",
    "- **动态值**：`nil`\n",
    "\n",
    "因为动态类型已经从 `nil` 变成了 `*TP`，所以 `err == nil` 的比较结果为 `false`，即接口不再视为 `nil`。然而，`err.Error()` 依旧可以调用，因为 `*TP` 的指针接收者实现了 `Error()` 方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为什么说是“陷阱”？\n",
    "\n",
    "- 很多开发者期望 “返回 nil 指针 → 接口也等于 nil”。\n",
    "   然而 Go 的接口判等遵循 “动态类型 + 动态值” 的模型，导致这类期望落空。\n",
    "- 如果程序逻辑里用 `if err != nil` 来判断是否有错误，那么这个接口会被误判为 “非 nil”，从而执行错误分支，甚至触发进一步的 nil 解引用或业务异常。\n",
    "\n",
    "\n",
    "\n",
    "避免方法\n",
    "\n",
    "1. **统一返回接口类型值**：让工厂函数直接返回 `error` 或其它接口，而不是指针类型。\n",
    "2. **使用显式的类型和值判断**：在逻辑中，若需要区分类型信息，可以用类型断言或 `reflect` 包查看真实类型。\n",
    "3. **构造函数保证非 nil 值**：比如返回具体的错误实例，或在确实没有对象时返回 `nil interface` 而不是 `nil` 指针。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type TP int\n",
    "\n",
    "func (tp *TP) Error() string { return \"TP\" }\n",
    "\n",
    "func main() {\n",
    "\tvar err error          // 起始为 nil 接口：动态类型和动态值都为 nil\n",
    "\tfmt.Printf(\"初始 err == nil: %v\\n\", err == nil)\n",
    "\n",
    "\tvar ptr *TP            // nil 指针，动态类型是 *TP\n",
    "\terr = ptr              // 接口绑定类型 *TP，动态值为 nil，但动态类型不是 nil\n",
    "\tfmt.Printf(\"赋值后 err == nil: %v\\n\", err == nil)\n",
    "\tfmt.Printf(\"err 的动态类型: %T\\n\", err)\n",
    "\n",
    "\t// 即使 err != nil，也能调用方法；此时使用的仍是 nil 指针\n",
    "\tfmt.Printf(\"err.Error(): %q\\n\", err.Error())\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "什么是 nil 接口（nil interface）？\n",
    "\n",
    "在 Go 语言中，**接口值由两部分组成：动态类型（dynamic type）和动态值（dynamic value）**。当我们说“接口是 nil”时，指的是这两部分同时为 `nil`。因此：\n",
    "\n",
    "> **“nil 接口” 就是指动态类型为 `nil`，动态值也为 `nil` 的接口值。**\n",
    "\n",
    "\n",
    "\n",
    "判等规则\n",
    "\n",
    "- **真正的 nil 接口**：动态类型 = `nil`，动态值 = `nil`。此时接口值完全为空，`if err == nil` 会判定为 `true`。\n",
    "- **非 nil，但底层值为 nil**：动态类型 ≠ `nil`，动态值 = `nil`。此时接口值不再是 nil 接口，`err == nil` 为 `false`，尽管底层实际是一个 `nil` 指针等引用类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初始 err == nil: true\n",
      "赋值后 err == nil: false\n",
      "err 的动态类型: *main.TP\n",
      "err.Error(): \"TP\"\n"
     ]
    }
   ],
   "source": [
    "// nil 接口 表示接口值中没有任何动态类型和值。当接口绑定了某个类型（即便底层值为 nil），它就不再是 nil 接口。这一概念贯穿 Go 的错误处理模式和接口设计，是掌握 Go 接口判等行为的关键。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type TP int\n",
    "\n",
    "func (tp *TP) Error() string { return \"TP\" }\n",
    "\n",
    "func main() {\n",
    "\tvar err error          // 起始为 nil 接口：动态类型和动态值都为 nil\n",
    "\tfmt.Printf(\"初始 err == nil: %v\\n\", err == nil)\n",
    "\n",
    "\tvar ptr *TP            // nil 指针，动态类型是 *TP\n",
    "\terr = ptr              // 接口绑定类型 *TP，动态值为 nil，但动态类型不是 nil\n",
    "\tfmt.Printf(\"赋值后 err == nil: %v\\n\", err == nil)\n",
    "\tfmt.Printf(\"err 的动态类型: %T\\n\", err)\n",
    "\n",
    "\t// 即使 err != nil，也能调用方法；此时使用的仍是 nil 指针\n",
    "\tfmt.Printf(\"err.Error(): %q\\n\", err.Error())\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 57 闭包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "func(参数列表) 返回类型 {\n",
    "    // 函数体\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 立即调用（IIFE：Immediately Invoked Function Expression）\n",
    "- 赋值给变量，让它在之后作为闭包调用\n",
    "- 将其作为参数或返回值传递"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, closure!\n",
      "7\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\t// 立即调用\n",
    "\tfunc(msg string) {\n",
    "\t\tfmt.Println(msg)\n",
    "\t}(\"Hello, closure!\")\n",
    "\n",
    "\t// 赋值给变量\n",
    "\tadd := func(a, b int) int {\n",
    "\t\treturn a + b\n",
    "\t}\n",
    "\tfmt.Println(add(3, 4))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Go 闭包概述\n",
    "\n",
    "- **闭包（Closure）** 是指函数值在被创建时捕获并记住了其外围词法作用域中的变量。即使外围函数已经返回，被捕获的变量依然与闭包函数共享。\n",
    "- Go 中的函数是一等公民，可以作为值传递、返回或赋给变量；这为闭包提供了载体。\n",
    "- 闭包捕获的是变量（引用），而不是变量当时的快照，因此后续对变量的修改会被闭包感知。\n",
    "- 典型用途包括：隐藏状态（如计数器）、延迟执行时引用外部变量、在并发或注册回调场景中携带上下文等。\n",
    "- 使用闭包时需注意循环变量捕获等常见陷阱；必要时通过引入局部变量来截断共享引用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 普通函数：不携带状态"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "4\n",
      "4\n"
     ]
    }
   ],
   "source": [
    "// 普通函数调用时，每次执行都有全新的局部变量，不会在多次调用之间共享状态\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func f() int {\n",
    "\ta := 3\n",
    "\ta++\n",
    "\treturn a\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(f()) // 4\n",
    "\tfmt.Println(f()) // 4 —— 每次调用都会重新创建局部变量 a\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 闭包保存状态（Stateful Closure）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "counterA: 1\n",
      "counterA: 2\n",
      "counterB: 1\n",
      "counterA: 3\n",
      "counterB: 2\n"
     ]
    }
   ],
   "source": [
    "// count 变量在 NewCounter 返回后仍被闭包引用，因此状态得以保留且在不同闭包实例之间相互独立。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "// NewCounter 返回一个闭包，每次调用都会递增内部计数器。\n",
    "func NewCounter() func() int {\n",
    "\tcount := 0\n",
    "\treturn func() int {\n",
    "\t\tcount++\n",
    "\t\treturn count\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tcounterA := NewCounter()\n",
    "\tcounterB := NewCounter()\n",
    "\n",
    "\tfmt.Println(\"counterA:\", counterA()) // 1\n",
    "\tfmt.Println(\"counterA:\", counterA()) // 2\n",
    "\tfmt.Println(\"counterB:\", counterB()) // 1\n",
    "\tfmt.Println(\"counterA:\", counterA()) // 3\n",
    "\tfmt.Println(\"counterB:\", counterB()) // 2\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 循环中的闭包捕获变量（经典陷阱与解决方案）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 错误示例 ===\n",
      "0\n",
      "1\n",
      "2\n",
      "\n",
      "=== 正确示例 ===\n",
      "0\n",
      "1\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "// 从 Go 1.22 开始，官方已经修复了“闭包捕获循环变量”这一老陷阱：每次循环迭代都会生成全新的循环变量副本。因此，即使是所谓的“错误示例”，现在也会输出 0 1 2，和“正确示例”一致，这就是你看到的现象。\n",
    "// Go ≤ 1.21 或 GODEBUG=loopvar=0：循环变量复用 → 错误示例输出 3 3 3。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"=== 错误示例 ===\")\n",
    "\tfuncs := make([]func(), 3) // 切片元素的类型是 “无参数、无返回值的函数” (func())，即函数类型。\n",
    "\tfor i := 0; i < 3; i++ {\n",
    "\t\tfuncs[i] = func() {\n",
    "\t\t\tfmt.Println(i) // 捕获的是同一个 i -> 三次都输出 3\n",
    "\t\t}\n",
    "\t}\n",
    "\tfor _, fn := range funcs {\n",
    "\t\tfn()\n",
    "\t}\n",
    "\n",
    "\tfmt.Println(\"\\n=== 正确示例 ===\")\n",
    "\tfuncsFixed := make([]func(), 3)\n",
    "\tfor i := 0; i < 3; i++ {\n",
    "\t\tj := i // 创建新的局部变量，截断捕获链\n",
    "\t\tfuncsFixed[i] = func() {\n",
    "\t\t\tfmt.Println(j) // 输出 0、1、2\n",
    "\t\t}\n",
    "\t}\n",
    "\tfor _, fn := range funcsFixed {\n",
    "\t\tfn()\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 序列生成：闭包驱动惰性序列"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1 1 2 3 5 8 13 21 34 55]\n"
     ]
    }
   ],
   "source": [
    "// 闭包可以维护内部状态，按需生成下一项，常用于生成器模式，如斐波那契数列。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func fibonacci() func() int {\n",
    "\ta, b := 0, 1\n",
    "\treturn func() int {\n",
    "\t\ta, b = b, a+b\n",
    "\t\treturn a\n",
    "\t}\n",
    "}\n",
    "\n",
    "func fibonacciSeq(n int) []int {\n",
    "\tnext := fibonacci()\n",
    "\tseq := make([]int, 0, n)\n",
    "\tfor i := 0; i < n; i++ {\n",
    "\t\tseq = append(seq, next())\n",
    "\t}\n",
    "\treturn seq\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(fibonacciSeq(10))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 并发场景：闭包在 goroutine 中携带上下文"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[goroutine 0] value=1\n",
      "[goroutine 1] value=2\n",
      "[goroutine 2] value=3\n",
      "[goroutine 3] value=4\n",
      "all goroutines done\n"
     ]
    }
   ],
   "source": [
    "// 闭包在并发执行时可携带每次迭代的变量，配合 WaitGroup 确保执行完成。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"sync\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func forRange() {\n",
    "\tarr := []int{1, 2, 3, 4}\n",
    "\twg := sync.WaitGroup{}\n",
    "\twg.Add(len(arr))\n",
    "\n",
    "\tfor idx, v := range arr {\n",
    "\t\tidx := idx // 显式拷贝，兼容旧版语义\n",
    "\t\tgo func(val int) {\n",
    "\t\t\tdefer wg.Done()\n",
    "\t\t\ttime.Sleep(time.Duration(idx+1) * 100 * time.Millisecond)\n",
    "\t\t\tfmt.Printf(\"[goroutine %d] value=%d\\n\", idx, val)\n",
    "\t\t}(v)\n",
    "\t}\n",
    "\n",
    "\twg.Wait()\n",
    "\tfmt.Println(\"all goroutines done\")\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tforRange()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 闭包作为延迟执行函数（defer/回调上下文）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Immediate: Bob\n",
      "Deferred: Bob\n"
     ]
    }
   ],
   "source": [
    "// 闭包与 defer 结合时，捕获的是变量引用。虽然 defer 在函数结束时才执行，但闭包读取的是最终的 name 值，即 Bob。若希望快照当前值，可在 defer 前创建局部副本。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tname := \"Alice\"\n",
    "\n",
    "\tdefer func() {\n",
    "\t\tfmt.Println(\"Deferred:\", name) // 捕获外部 name\n",
    "\t}()\n",
    "\n",
    "\tname = \"Bob\"\n",
    "\tfmt.Println(\"Immediate:\", name)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 58 指针总结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 指针的本质"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0xc0000100f0\n",
      "42\n",
      "100\n"
     ]
    }
   ],
   "source": [
    "// 指针是保存内存地址的变量。通过指针可以间接访问（读取或修改）该地址处存储的数据，本身并不存储数据内容，而是“指向”数据所在的位置。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tx := 42           // 普通整型变量\n",
    "\tptr := &x         // 取地址运算符 &，获得 x 的内存地址\n",
    "\tfmt.Println(ptr)  // 打印地址\n",
    "\tfmt.Println(*ptr) // 解引用运算符 *，读取地址处的值\n",
    "\n",
    "\t*ptr = 100        // 通过指针修改原变量\n",
    "\tfmt.Println(x)    // 100\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 指针的基本操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true\n",
      "7\n",
      "\n",
      "Go Pointer\n"
     ]
    }
   ],
   "source": [
    "// 指针的常见操作包括：声明 var p *T、取地址 &value、解引用 *p、使用内建函数 new(T) 分配零值并返回指针\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func main() {\n",
    "\tvar p *int          // 声明指向 int 的指针，当前为 nil\n",
    "\tfmt.Println(p == nil)\n",
    "\n",
    "\tv := 7\n",
    "\tp = &v              // 指向现有变量\n",
    "\tfmt.Println(*p)     // 7\n",
    "\n",
    "\tq := new(string)    // new 返回指向零值的指针\n",
    "\tfmt.Println(*q)     // 空字符串\n",
    "\t*q = \"Go Pointer\"\n",
    "\tfmt.Println(*q)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 函数何时传指针，何时返回指针"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{api 8080}\n",
      "admin 80\n"
     ]
    }
   ],
   "source": [
    "// 传指针：当需要在函数内修改外部变量，或传递大对象以避免拷贝时。\n",
    "// 返回指针：当需要让调用者得到函数内部创建的数据的引用，并保持其生命周期。Go 会做逃逸分析确保安全。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Config struct {\n",
    "\tName string\n",
    "\tPort int\n",
    "}\n",
    "\n",
    "func setup(cfg *Config) { // 传指针允许修改调用者结构体\n",
    "\tcfg.Port = 8080\n",
    "}\n",
    "\n",
    "func newConfig(name string) *Config { // 返回指针，保持状态\n",
    "\treturn &Config{Name: name, Port: 80}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tcfg := Config{Name: \"api\"}\n",
    "\tsetup(&cfg)\n",
    "\tfmt.Println(cfg) // {api 8080}\n",
    "\n",
    "\tappCfg := newConfig(\"admin\")\n",
    "\tfmt.Println(appCfg.Name, appCfg.Port) // admin 80\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 结构体的方法何时接受指针"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "// 若方法需要修改接收者或避免结构体拷贝，应使用指针接收者 func (p *Type) Method()；仅读取且结构体较小、不可变场景可用值接收者。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Counter struct {\n",
    "\tvalue int\n",
    "}\n",
    "\n",
    "func (c *Counter) Inc() { // 指针接收者：修改内部状态\n",
    "\tc.value++\n",
    "}\n",
    "\n",
    "func (c Counter) Value() int { // 值接收者：只读\n",
    "\treturn c.value\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvar c Counter\n",
    "\tc.Inc()\n",
    "\tfmt.Println(c.Value()) // 1\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 何时需要给函数传切片的指针"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1 2 3 4 5]\n"
     ]
    }
   ],
   "source": [
    "// 切片本身是一个三元描述符（指向底层数组的指针、长度、容量）。大多数场景传值即可；但当函数需要“替换”切片描述符本身（例如重新分配底层数组并让调用者看到变化）时，可传 *[]T。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func appendAndReplace(dst *[]int, elems ...int) {\n",
    "\t// 重新构造一个新切片并赋值回指针，以便调用者获得最新描述符\n",
    "\tnewSlice := append([]int{}, (*dst)...)\n",
    "\tnewSlice = append(newSlice, elems...)\n",
    "\t*dst = newSlice\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tnums := []int{1, 2}\n",
    "\tappendAndReplace(&nums, 3, 4, 5)\n",
    "\tfmt.Println(nums) // [1 2 3 4 5]\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 遍历 slice/map 的同时修改其中的元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[10 20 30]\n",
      "map[apple:7 banana:5]\n"
     ]
    }
   ],
   "source": [
    "// 遍历切片时可使用索引访问并直接修改元素；需注意不能在 range 循环中取元素地址持久化（因元素可能是临时拷贝）。\n",
    "// 遍历 map 时可通过 key 写入新值，但遍历期间增删需要小心（允许修改已有键对应的值）。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "func mutateSlice() {\n",
    "\tnums := []int{1, 2, 3}\n",
    "\tfor i := range nums { // 使用索引修改底层数组\n",
    "\t\tnums[i] *= 10\n",
    "\t}\n",
    "\tfmt.Println(nums) // [10 20 30]\n",
    "}\n",
    "\n",
    "func mutateMap() {\n",
    "\tstock := map[string]int{\"apple\": 5, \"banana\": 3}\n",
    "\tfor k := range stock {\n",
    "\t\tstock[k] += 2 // 修改已有键对应的值\n",
    "\t}\n",
    "\tfmt.Println(stock) // map[apple:7 banana:5]\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tmutateSlice()\n",
    "\tmutateMap()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 59 泛型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 概述\n",
    "\n",
    "- **引入背景**：自 Go 1.18 起支持类型参数，使函数与类型在保持静态类型安全的同时具备更强的复用能力。\n",
    "- **核心元素**\n",
    "  - **类型参数列表**：写在方括号 `[]` 中，如 `func Foo[T any](x T)`.\n",
    "  - **约束（Constraints）**：限制类型参数可接受的类型范围，来自接口或 `constraints` 包，如 `constraints.Ordered`.\n",
    "  - **类型推断**：调用方可省略类型参数，编译器根据实参类型推断。\n",
    "  - **字面值与接口兼容**：类型参数与普通接口配合使用，支持组合。\n",
    "- **设计理念**：保持语言简洁，泛型并非面向所有需求，而是解决“写一次、适用于多类型”且无法通过接口或代码生成友好解决的场景。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 示例"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7\n",
      "3.14\n",
      "gopher\n"
     ]
    }
   ],
   "source": [
    "// 使用标准库 constraints 提供的 Ordered 约束，实现可比较类型的最大值函数。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\n",
    "\t\"golang.org/x/exp/constraints\"\n",
    ")\n",
    "\n",
    "func Max[T constraints.Ordered](a, b T) T {\n",
    "\tif a > b {\n",
    "\t\treturn a\n",
    "\t}\n",
    "\treturn b\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(Max(3, 7))             // int\n",
    "\tfmt.Println(Max(3.14, 2.71))       // float64\n",
    "\tfmt.Println(Max(\"go\", \"gopher\"))   // string\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 泛型类型（栈结构）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "int pop: 20\n",
      "string length: 2\n"
     ]
    }
   ],
   "source": [
    "// 自定义一个泛型栈，支持任意元素类型。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Stack[T any] struct {\n",
    "\tdata []T\n",
    "}\n",
    "\n",
    "func (s *Stack[T]) Push(v T) {\n",
    "\ts.data = append(s.data, v)\n",
    "}\n",
    "\n",
    "func (s *Stack[T]) Pop() (T, bool) {\n",
    "\tif len(s.data) == 0 {\n",
    "\t\tvar zero T\n",
    "\t\treturn zero, false\n",
    "\t}\n",
    "\tlast := len(s.data) - 1\n",
    "\tval := s.data[last]\n",
    "\ts.data = s.data[:last]\n",
    "\treturn val, true\n",
    "}\n",
    "\n",
    "func (s *Stack[T]) Len() int {\n",
    "\treturn len(s.data)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tvar intStack Stack[int]\n",
    "\tintStack.Push(10)\n",
    "\tintStack.Push(20)\n",
    "\tif v, ok := intStack.Pop(); ok {\n",
    "\t\tfmt.Println(\"int pop:\", v)\n",
    "\t}\n",
    "\n",
    "\tvar strStack Stack[string]\n",
    "\tstrStack.Push(\"hello\")\n",
    "\tstrStack.Push(\"generics\")\n",
    "\tfmt.Println(\"string length:\", strStack.Len())\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 自定义约束与类型推断"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "18\n",
      "10.2\n"
     ]
    }
   ],
   "source": [
    "// 定义自有约束接口 Number，用于实现泛型求和函数。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Number interface {\n",
    "\t~int | ~int64 | ~float64 // 使用近似类型集允许自定义别名\n",
    "}\n",
    "\n",
    "func Sum[T Number](values ...T) T {\n",
    "\tvar total T\n",
    "\tfor _, v := range values {\n",
    "\t\ttotal += v\n",
    "\t}\n",
    "\treturn total\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(Sum(1, 2, 3, 4))         // 推断为 int\n",
    "\tfmt.Println(Sum[int64](5, 6, 7))     // 显式指定 int64\n",
    "\tfmt.Println(Sum(1.2, 3.4, 5.6))      // 推断为 float64\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 类型联合约束的泛型函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "user resp status: 403 err: <nil>\n",
      "book resp status: 403 err: <nil>\n"
     ]
    }
   ],
   "source": [
    "// 函数 httpRPC 的类型参数 T 被限制在两个结构体类型的联合中，代表“只能传这两种请求”。\n",
    "// 结合 reflect.TypeOf 和 switch，实现不同类型请求到不同 URL 的路由。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"bytes\"\n",
    "\t\"encoding/json\"\n",
    "\t\"fmt\"\n",
    "\t\"net/http/httptest\"\n",
    "\t\"net/http\"\n",
    "\t\"reflect\"\n",
    ")\n",
    "\n",
    "type GetUserRequest struct {\n",
    "\tID int\n",
    "}\n",
    "\n",
    "type GetBookRequest struct {\n",
    "\tISBN string\n",
    "}\n",
    "\n",
    "func httpRPC[T GetUserRequest | GetBookRequest](request T, client *http.Client) (*http.Response, error) {\n",
    "\turl := \"http://example.com/\"\n",
    "\tswitch reflect.TypeOf(request).Name() {\n",
    "\tcase \"GetUserRequest\":\n",
    "\t\turl += \"user\"\n",
    "\tcase \"GetBookRequest\":\n",
    "\t\turl += \"book\"\n",
    "\tdefault:\n",
    "\t\treturn nil, fmt.Errorf(\"unsupported request type\")\n",
    "\t}\n",
    "\n",
    "\tpayload, err := json.Marshal(request)\n",
    "\tif err != nil {\n",
    "\t\treturn nil, err\n",
    "\t}\n",
    "\treturn client.Post(url, \"application/json\", bytes.NewReader(payload))\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\t// 使用 httptest 创建测试服务器，避免真实网络请求\n",
    "\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n",
    "\t\tfmt.Println(\"POST\", r.URL.Path)\n",
    "\t\tw.WriteHeader(http.StatusOK)\n",
    "\t}))\n",
    "\tdefer ts.Close()\n",
    "\n",
    "\tclient := ts.Client()\n",
    "\n",
    "\tresp1, err1 := httpRPC(GetUserRequest{ID: 42}, client)\n",
    "\tfmt.Println(\"user resp status:\", resp1.StatusCode, \"err:\", err1)\n",
    "\n",
    "\tresp2, err2 := httpRPC(GetBookRequest{ISBN: \"978-7-302-16901-5\"}, client)\n",
    "\tfmt.Println(\"book resp status:\", resp2.StatusCode, \"err:\", err2)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 泛型与 map/slice 组合 —— 缓存容器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Alice age: 30\n",
      "token-xyz true\n"
     ]
    }
   ],
   "source": [
    "// 使用类型参数同时限定键和值，构建简单缓存。\n",
    "\n",
    "package main\n",
    "\n",
    "import \"fmt\"\n",
    "\n",
    "type Cache[K comparable, V any] struct {\n",
    "\tstore map[K]V\n",
    "}\n",
    "\n",
    "func NewCache[K comparable, V any]() *Cache[K, V] {\n",
    "\treturn &Cache[K, V]{store: make(map[K]V)}\n",
    "}\n",
    "\n",
    "func (c *Cache[K, V]) Set(key K, value V) {\n",
    "\tc.store[key] = value\n",
    "}\n",
    "\n",
    "func (c *Cache[K, V]) Get(key K) (V, bool) {\n",
    "\tval, ok := c.store[key]\n",
    "\treturn val, ok\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tuserAge := NewCache[string, int]()\n",
    "\tuserAge.Set(\"Alice\", 30)\n",
    "\tuserAge.Set(\"Bob\", 27)\n",
    "\n",
    "\tif age, ok := userAge.Get(\"Alice\"); ok {\n",
    "\t\tfmt.Println(\"Alice age:\", age)\n",
    "\t}\n",
    "\n",
    "\tsessionCache := NewCache[int, string]()\n",
    "\tsessionCache.Set(1001, \"token-xyz\")\n",
    "\tfmt.Println(sessionCache.Get(1001))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 60 练习：基于泛型实现Set"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- [代码](./60/type_alias/set.go)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 运行"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Len: 3\n",
      "Exists 3: true\n",
      "Exists 2: false\n",
      "Range 遍历：\n",
      "  elem: 1\n",
      "  elem: 3\n",
      "  elem: 4\n",
      "Sorted elements: [1 3 4]\n",
      "Union: [Java Go Rust Python]\n",
      "Intersect: [Go Rust]\n",
      "Difference: [Java]\n",
      "SymmetricDifference: [Java Python]\n",
      "inter 是 union 的子集\n",
      "原集合是否包含 999？ false\n"
     ]
    }
   ],
   "source": [
    "! cd 60/ && go run ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 单元测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== RUN   TestNewSetInitializesWithUniqueElements\n",
      "--- PASS: TestNewSetInitializesWithUniqueElements (0.00s)\n",
      "=== RUN   TestSetAdd\n",
      "--- PASS: TestSetAdd (0.00s)\n",
      "=== RUN   TestSetAddAll\n",
      "--- PASS: TestSetAddAll (0.00s)\n",
      "=== RUN   TestSetRemove\n",
      "--- PASS: TestSetRemove (0.00s)\n",
      "=== RUN   TestSetExists\n",
      "--- PASS: TestSetExists (0.00s)\n",
      "=== RUN   TestSetLen\n",
      "--- PASS: TestSetLen (0.00s)\n",
      "=== RUN   TestSetClear\n",
      "--- PASS: TestSetClear (0.00s)\n",
      "=== RUN   TestSetRangeEarlyExit\n",
      "--- PASS: TestSetRangeEarlyExit (0.00s)\n",
      "=== RUN   TestSetElementsReturnsCopy\n",
      "--- PASS: TestSetElementsReturnsCopy (0.00s)\n",
      "=== RUN   TestSetSortedElements\n",
      "--- PASS: TestSetSortedElements (0.00s)\n",
      "=== RUN   TestSetCloneAdditionsIndependent\n",
      "--- PASS: TestSetCloneAdditionsIndependent (0.00s)\n",
      "=== RUN   TestSetCloneRemovalsIndependent\n",
      "--- PASS: TestSetCloneRemovalsIndependent (0.00s)\n",
      "=== RUN   TestSetMapReturnsCopy\n",
      "--- PASS: TestSetMapReturnsCopy (0.00s)\n",
      "=== RUN   TestUnion\n",
      "--- PASS: TestUnion (0.00s)\n",
      "=== RUN   TestIntersect\n",
      "--- PASS: TestIntersect (0.00s)\n",
      "=== RUN   TestDifference\n",
      "--- PASS: TestDifference (0.00s)\n",
      "=== RUN   TestSymmetricDifference\n",
      "--- PASS: TestSymmetricDifference (0.00s)\n",
      "=== RUN   TestIsSubsetTrue\n",
      "--- PASS: TestIsSubsetTrue (0.00s)\n",
      "=== RUN   TestIsSubsetFalse\n",
      "--- PASS: TestIsSubsetFalse (0.00s)\n",
      "PASS\n",
      "ok  \tgeneric/type_alias\t0.004s\n"
     ]
    }
   ],
   "source": [
    "! cd 60/ && go test ./type_alias -v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== RUN   TestSetAdd\n",
      "--- PASS: TestSetAdd (0.00s)\n",
      "PASS\n",
      "ok  \tgeneric/type_alias\t0.004s\n"
     ]
    }
   ],
   "source": [
    "! cd 60/ && go test -v ./type_alias -run=^TestSetAdd$ -count=1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- BenchmarkSet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "goos: linux\n",
      "goarch: amd64\n",
      "pkg: generic/type_alias\n",
      "cpu: AMD Ryzen 7 7840HS with Radeon 780M Graphics   \n",
      "BenchmarkSetExists-16    \t236088787\t         5.026 ns/op\n",
      "PASS\n",
      "ok  \tgeneric/type_alias\t1.193s\n"
     ]
    }
   ],
   "source": [
    "! cd 60/ && go test ./type_alias -bench=^BenchmarkSetExists$ -run=^$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 61 随机数种子"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "“随机数种子”指的是伪随机数生成器（PRNG）的起始状态；相同的种子会产生完全相同的序列，不同的种子会产生不同的序列。下面用 Go 的标准库 `math/rand` 演示三种经典场景，每段代码都可直接运行。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 默认种子（Go 自带的固定种子）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "默认种子（固定序列）:\n",
      "62 64 12 61 92 \n"
     ]
    }
   ],
   "source": [
    "// 不显式设置种子时，math/rand 内部使用一个固定种子，因此每次运行程序都会得到相同的结果。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"math/rand\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"默认种子（固定序列）:\")\n",
    "\tfor i := 0; i < 5; i++ {\n",
    "\t\tfmt.Printf(\"%d \", rand.Intn(100))\n",
    "\t}\n",
    "\tfmt.Println()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 固定种子（可复现的序列）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "固定种子（可复现输出）:\n",
      "5 87 68 50 23 \n"
     ]
    }
   ],
   "source": [
    "// 显式指定一个常量种子（例如 42），便于调试时复现同样的伪随机序列。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"math/rand\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\trng := rand.New(rand.NewSource(42))\n",
    "\tfmt.Println(\"固定种子（可复现输出）:\")\n",
    "\tfor i := 0; i < 5; i++ {\n",
    "\t\tfmt.Printf(\"%d \", rng.Intn(100))\n",
    "\t}\n",
    "\tfmt.Println()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 时间种子（每次运行不同）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "时间种子（每次运行不同）:\n",
      "21 23 53 78 89 \n"
     ]
    }
   ],
   "source": [
    "// 使用当前时间（通常是纳秒时间戳）作为种子，每次运行都会产生不同的伪随机序列，是最常见的“随机化”方式。\n",
    "\n",
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"math/rand\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\trng := rand.New(rand.NewSource(time.Now().UnixNano()))\n",
    "\tfmt.Println(\"时间种子（每次运行不同）:\")\n",
    "\tfor i := 0; i < 5; i++ {\n",
    "\t\tfmt.Printf(\"%d \", rng.Intn(100))\n",
    "\t}\n",
    "\tfmt.Println()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "补充说明\n",
    "\n",
    "- 以上示例均使用 `math/rand`，适用于模拟、游戏、算法测试等非安全场景。\n",
    "- 如果需要密码学安全的随机数，请改用 `crypto/rand`，它不需要手动设置种子，内部直接从操作系统的真随机源读取数据。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 对比“重建 Source”与“复用 Source”得到的序列"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== 重建 Source（每次序列都从头开始） ===\n",
      "run 1 => 60\n",
      "run 2 => 60\n",
      "run 3 => 60\n",
      "\n",
      "=== 复用同一个 Source（序列会连续推进） ===\n",
      "draw 1 => 60\n",
      "draw 2 => 56\n",
      "draw 3 => 81\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\n",
    "\trandv2 \"math/rand/v2\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tconst n = 3\n",
    "\n",
    "\tfmt.Println(\"=== 重建 Source（每次序列都从头开始） ===\")\n",
    "\tfor i := 0; i < n; i++ {\n",
    "\t\tsrc := randv2.NewPCG(123, 456)\n",
    "\t\trng := randv2.New(src)\n",
    "\t\tfmt.Printf(\"run %d => %d\\n\", i+1, rng.IntN(100))\n",
    "\t}\n",
    "\n",
    "\tfmt.Println(\"\\n=== 复用同一个 Source（序列会连续推进） ===\")\n",
    "\tsrc := randv2.NewPCG(123, 456)\n",
    "\trng := randv2.New(src)\n",
    "\tfor i := 0; i < n; i++ {\n",
    "\t\tfmt.Printf(\"draw %d => %d\\n\", i+1, rng.IntN(100))\n",
    "\t}\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 在 `main` 中手动跑“基准”，比较 `IntN` 与 `ExpFloat64`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Go testing.Benchmark 手动运行示例（次数自动调整）\n",
      "IntN:\n",
      "  ns/op   = 4\n",
      "  bytes/op= 0\n",
      "  allocs  = 0\n",
      "\n",
      "ExpFloat64:\n",
      "  ns/op   = 6\n",
      "  bytes/op= 0\n",
      "  allocs  = 0\n",
      "\n",
      "总耗时：3.557666072s\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"testing\"\n",
    "\t\"time\"\n",
    "\n",
    "\trandv2 \"math/rand/v2\"\n",
    ")\n",
    "\n",
    "func benchmarkIntN(b *testing.B) {\n",
    "\tfor i := 0; i < b.N; i++ {\n",
    "\t\t_ = randv2.IntN(1_000_000)\n",
    "\t}\n",
    "}\n",
    "\n",
    "func benchmarkExpFloat64(b *testing.B) {\n",
    "\tfor i := 0; i < b.N; i++ {\n",
    "\t\t_ = randv2.ExpFloat64()\n",
    "\t}\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tfmt.Println(\"Go testing.Benchmark 手动运行示例（次数自动调整）\")\n",
    "\n",
    "\tprintResult := func(name string, result testing.BenchmarkResult) {\n",
    "\t\tfmt.Printf(\"%s:\\n\", name)\n",
    "\t\tfmt.Printf(\"  ns/op   = %v\\n\", result.NsPerOp())\n",
    "\t\tfmt.Printf(\"  bytes/op= %v\\n\", result.AllocedBytesPerOp())\n",
    "\t\tfmt.Printf(\"  allocs  = %v\\n\\n\", result.AllocsPerOp())\n",
    "\t}\n",
    "\n",
    "\tstart := time.Now()\n",
    "\tprintResult(\"IntN\", testing.Benchmark(benchmarkIntN))\n",
    "\tprintResult(\"ExpFloat64\", testing.Benchmark(benchmarkExpFloat64))\n",
    "\tfmt.Printf(\"总耗时：%s\\n\", time.Since(start))\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 并发环境下使用 `NewLockedSource`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "worker 3 -> 735\n",
      "worker 3 -> 517\n",
      "worker 3 -> 676\n",
      "worker 3 -> 61\n",
      "worker 3 -> 97\n",
      "worker 0 -> 305\n",
      "worker 0 -> 148\n",
      "worker 0 -> 606\n",
      "worker 0 -> 270\n",
      "worker 0 -> 822\n",
      "worker 2 -> 256\n",
      "worker 2 -> 744\n",
      "worker 2 -> 93\n",
      "worker 2 -> 274\n",
      "worker 2 -> 231\n",
      "worker 1 -> 360\n",
      "worker 1 -> 349\n",
      "worker 1 -> 181\n",
      "worker 1 -> 414\n",
      "worker 1 -> 900\n",
      "\n",
      "所有随机数（无序）：\n",
      "735 517 676 61 97 305 148 606 270 822 256 744 93 274 231 360 349 181 414 900 \n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"sync\"\n",
    "\n",
    "\trandv2 \"math/rand/v2\"\n",
    ")\n",
    "\n",
    "// lockedRand 在内部对 randv2.Rand 做互斥保护。\n",
    "type lockedRand struct {\n",
    "\tmu sync.Mutex\n",
    "\tr  *randv2.Rand\n",
    "}\n",
    "\n",
    "func newLockedRand(seedHi, seedLo uint64) *lockedRand {\n",
    "\tsrc := randv2.NewPCG(seedHi, seedLo)\n",
    "\treturn &lockedRand{r: randv2.New(src)}\n",
    "}\n",
    "\n",
    "func (lr *lockedRand) IntN(n int) int {\n",
    "\tlr.mu.Lock()\n",
    "\tdefer lr.mu.Unlock()\n",
    "\treturn lr.r.IntN(n)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tconst (\n",
    "\t\tworkers   = 4\n",
    "\t\tperWorker = 5\n",
    "\t)\n",
    "\n",
    "\tlr := newLockedRand(987654321, 123456789)\n",
    "\n",
    "\tresults := make(chan int, workers*perWorker)\n",
    "\tvar wg sync.WaitGroup\n",
    "\n",
    "\tfor w := 0; w < workers; w++ {\n",
    "\t\twg.Add(1)\n",
    "\t\tgo func(id int) {\n",
    "\t\t\tdefer wg.Done()\n",
    "\t\t\tfor i := 0; i < perWorker; i++ {\n",
    "\t\t\t\tval := lr.IntN(1_000)\n",
    "\t\t\t\tfmt.Printf(\"worker %d -> %d\\n\", id, val)\n",
    "\t\t\t\tresults <- val\n",
    "\t\t\t}\n",
    "\t\t}(w)\n",
    "\t}\n",
    "\n",
    "\twg.Wait()\n",
    "\tclose(results)\n",
    "\n",
    "\tfmt.Println(\"\\n所有随机数（无序）：\")\n",
    "\tfor v := range results {\n",
    "\t\tfmt.Printf(\"%d \", v)\n",
    "\t}\n",
    "\tfmt.Println()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 62* 练习：生成随机字符串"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 63 内存逃逸与垃圾回收"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 内存逃逸（Escape Analysis）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Go 编译器在编译期会进行 **逃逸分析**，判断变量应分配在栈上还是堆上。如果某个变量的地址可能在当前函数之外被使用（例如返回给调用者、存入全局变量、接口等），编译器就会让该变量“逃逸”到堆上，从而避免悬垂指针。堆上分配的对象需要由垃圾回收器管理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "stack value: 42\n",
      "heap value: 99\n",
      "interface holds: {7}\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    ")\n",
    "\n",
    "// Data 是一个简单结构体\n",
    "type Data struct {\n",
    "\tvalue int\n",
    "}\n",
    "\n",
    "// stackAllocated 中的 d 只在函数内部使用，不会逃逸\n",
    "func stackAllocated() {\n",
    "\td := Data{value: 42}\n",
    "\tfmt.Println(\"stack value:\", d.value)\n",
    "}\n",
    "\n",
    "// heapAllocated 返回局部变量的指针，d 会逃逸到堆上\n",
    "func heapAllocated() *Data {\n",
    "\td := Data{value: 99}\n",
    "\treturn &d // 返回指针，触发逃逸\n",
    "}\n",
    "\n",
    "// interfaceEscape 将局部变量赋值给 interface{}，也会触发逃逸\n",
    "func interfaceEscape() {\n",
    "\td := Data{value: 7}\n",
    "\tvar any interface{} = d // d 会被复制到堆上\n",
    "\tfmt.Println(\"interface holds:\", any)\n",
    "}\n",
    "\n",
    "func main() {\n",
    "\tstackAllocated()\n",
    "\tp := heapAllocated()\n",
    "\tfmt.Println(\"heap value:\", p.value)\n",
    "\n",
    "\tinterfaceEscape()\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 查看逃逸分析报告"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "# command-line-arguments\n",
      "./main.go:13:6: cannot inline stackAllocated: function too complex: cost 88 exceeds budget 80\n",
      "./main.go:19:6: can inline heapAllocated with cost 10 as: func() *Data { d := Data{...}; return &d }\n",
      "./main.go:25:6: cannot inline interfaceEscape: function too complex: cost 92 exceeds budget 80\n",
      "./main.go:31:6: cannot inline main: function too complex: cost 215 exceeds budget 80\n",
      "./main.go:15:13: inlining call to fmt.Println\n",
      "./main.go:28:13: inlining call to fmt.Println\n",
      "./main.go:33:20: inlining call to heapAllocated\n",
      "./main.go:34:13: inlining call to fmt.Println\n",
      "./main.go:15:31: d.value escapes to heap:\n",
      "./main.go:15:31:   flow: {storage for ... argument} = &{storage for d.value}:\n",
      "./main.go:15:31:     from d.value (spill) at ./main.go:15:31\n",
      "./main.go:15:31:     from ... argument (slice-literal-element) at ./main.go:15:13\n",
      "./main.go:15:31:   flow: fmt.a = &{storage for ... argument}:\n",
      "./main.go:15:31:     from ... argument (spill) at ./main.go:15:13\n",
      "./main.go:15:31:     from fmt.a := ... argument (assign-pair) at ./main.go:15:13\n",
      "./main.go:15:31:   flow: {heap} = *fmt.a:\n",
      "./main.go:15:31:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:15:13\n",
      "./main.go:15:14: \"stack value:\" escapes to heap:\n",
      "./main.go:15:14:   flow: {storage for ... argument} = &{storage for \"stack value:\"}:\n",
      "./main.go:15:14:     from \"stack value:\" (spill) at ./main.go:15:14\n",
      "./main.go:15:14:     from ... argument (slice-literal-element) at ./main.go:15:13\n",
      "./main.go:15:14:   flow: fmt.a = &{storage for ... argument}:\n",
      "./main.go:15:14:     from ... argument (spill) at ./main.go:15:13\n",
      "./main.go:15:14:     from fmt.a := ... argument (assign-pair) at ./main.go:15:13\n",
      "./main.go:15:14:   flow: {heap} = *fmt.a:\n",
      "./main.go:15:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:15:13\n",
      "./main.go:15:13: ... argument does not escape\n",
      "./main.go:15:14: \"stack value:\" escapes to heap\n",
      "./main.go:15:31: d.value escapes to heap\n",
      "./main.go:20:2: d escapes to heap:\n",
      "./main.go:20:2:   flow: ~r0 = &d:\n",
      "./main.go:20:2:     from &d (address-of) at ./main.go:21:9\n",
      "./main.go:20:2:     from return &d (return) at ./main.go:21:2\n",
      "./main.go:20:2: moved to heap: d\n",
      "./main.go:27:24: d escapes to heap:\n",
      "./main.go:27:24:   flow: any = &{storage for d}:\n",
      "./main.go:27:24:     from d (spill) at ./main.go:27:24\n",
      "./main.go:27:24:     from any := d (assign) at ./main.go:27:6\n",
      "./main.go:27:24:   flow: {storage for ... argument} = any:\n",
      "./main.go:27:24:     from ... argument (slice-literal-element) at ./main.go:28:13\n",
      "./main.go:27:24:   flow: fmt.a = &{storage for ... argument}:\n",
      "./main.go:27:24:     from ... argument (spill) at ./main.go:28:13\n",
      "./main.go:27:24:     from fmt.a := ... argument (assign-pair) at ./main.go:28:13\n",
      "./main.go:27:24:   flow: {heap} = *fmt.a:\n",
      "./main.go:27:24:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:28:13\n",
      "./main.go:28:14: \"interface holds:\" escapes to heap:\n",
      "./main.go:28:14:   flow: {storage for ... argument} = &{storage for \"interface holds:\"}:\n",
      "./main.go:28:14:     from \"interface holds:\" (spill) at ./main.go:28:14\n",
      "./main.go:28:14:     from ... argument (slice-literal-element) at ./main.go:28:13\n",
      "./main.go:28:14:   flow: fmt.a = &{storage for ... argument}:\n",
      "./main.go:28:14:     from ... argument (spill) at ./main.go:28:13\n",
      "./main.go:28:14:     from fmt.a := ... argument (assign-pair) at ./main.go:28:13\n",
      "./main.go:28:14:   flow: {heap} = *fmt.a:\n",
      "./main.go:28:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:28:13\n",
      "./main.go:27:24: d escapes to heap\n",
      "./main.go:28:13: ... argument does not escape\n",
      "./main.go:28:14: \"interface holds:\" escapes to heap\n",
      "./main.go:34:30: p.value escapes to heap:\n",
      "./main.go:34:30:   flow: {storage for ... argument} = &{storage for p.value}:\n",
      "./main.go:34:30:     from p.value (spill) at ./main.go:34:30\n",
      "./main.go:34:30:     from ... argument (slice-literal-element) at ./main.go:34:13\n",
      "./main.go:34:30:   flow: fmt.a = &{storage for ... argument}:\n",
      "./main.go:34:30:     from ... argument (spill) at ./main.go:34:13\n",
      "./main.go:34:30:     from fmt.a := ... argument (assign-pair) at ./main.go:34:13\n",
      "./main.go:34:30:   flow: {heap} = *fmt.a:\n",
      "./main.go:34:30:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:34:13\n",
      "./main.go:34:14: \"heap value:\" escapes to heap:\n",
      "./main.go:34:14:   flow: {storage for ... argument} = &{storage for \"heap value:\"}:\n",
      "./main.go:34:14:     from \"heap value:\" (spill) at ./main.go:34:14\n",
      "./main.go:34:14:     from ... argument (slice-literal-element) at ./main.go:34:13\n",
      "./main.go:34:14:   flow: fmt.a = &{storage for ... argument}:\n",
      "./main.go:34:14:     from ... argument (spill) at ./main.go:34:13\n",
      "./main.go:34:14:     from fmt.a := ... argument (assign-pair) at ./main.go:34:13\n",
      "./main.go:34:14:   flow: {heap} = *fmt.a:\n",
      "./main.go:34:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:34:13\n",
      "./main.go:34:13: ... argument does not escape\n",
      "./main.go:34:14: \"heap value:\" escapes to heap\n",
      "./main.go:34:30: p.value escapes to heap\n"
     ]
    }
   ],
   "source": [
    "! cd ./63 && go build -gcflags=\"-m=2\" main.go "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 垃圾回收（Garbage Collection, GC）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Go 使用并发标记-清扫（mark-and-sweep）的垃圾回收器，自动释放不再被引用的堆对象。程序无需手动 `free` 内存，但可以通过 `runtime.GC()` 主动触发一次 GC，或通过 `runtime.ReadMemStats` 读取 GC 统计数据。理解 GC 有助于编写更高效、低延迟的程序。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "程序启动: HeapAlloc=184 KB  NumGC=0\n",
      "第 1 轮分配后: HeapAlloc=187 KB  NumGC=0\n",
      "第 2 轮分配后: HeapAlloc=187 KB  NumGC=0\n",
      "第 3 轮分配后: HeapAlloc=187 KB  NumGC=0\n",
      "\n",
      "显式触发 GC ...\n",
      "显式 GC 之后: HeapAlloc=194 KB  NumGC=1\n"
     ]
    }
   ],
   "source": [
    "package main\n",
    "\n",
    "import (\n",
    "\t\"fmt\"\n",
    "\t\"runtime\"\n",
    "\t\"time\"\n",
    ")\n",
    "\n",
    "func main() {\n",
    "\tprintMemStats(\"程序启动\")\n",
    "\n",
    "\t// 连续分配大量临时切片，制造“垃圾”\n",
    "\tfor i := 0; i < 3; i++ {\n",
    "\t\tcreateGarbage(10_0000) // 100k\n",
    "\t\tprintMemStats(fmt.Sprintf(\"第 %d 轮分配后\", i+1))\n",
    "\t\ttime.Sleep(200 * time.Millisecond)\n",
    "\t}\n",
    "\n",
    "\tfmt.Println(\"\\n显式触发 GC ...\")\n",
    "\truntime.GC() // 主动触发垃圾回收\n",
    "\tprintMemStats(\"显式 GC 之后\")\n",
    "}\n",
    "\n",
    "// createGarbage 创建大量临时数据并立刻丢弃\n",
    "func createGarbage(n int) {\n",
    "\tfor i := 0; i < n; i++ {\n",
    "\t\t// 创建一个 1 KB 的切片\n",
    "\t\t_ = make([]byte, 1024)\n",
    "\t}\n",
    "}\n",
    "\n",
    "// printMemStats 打印关键的 GC / 内存指标\n",
    "func printMemStats(tag string) {\n",
    "\tvar m runtime.MemStats\n",
    "\truntime.ReadMemStats(&m)\n",
    "\tfmt.Printf(\n",
    "\t\t\"%s: HeapAlloc=%v KB  NumGC=%d\\n\",\n",
    "\t\ttag,\n",
    "\t\tm.HeapAlloc/1024,\n",
    "\t\tm.NumGC,\n",
    "\t)\n",
    "}"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Go (gonb)",
   "language": "go",
   "name": "gonb"
  },
  "language_info": {
   "codemirror_mode": "",
   "file_extension": ".go",
   "mimetype": "text/x-go",
   "name": "go",
   "nbconvert_exporter": "",
   "pygments_lexer": "",
   "version": "go1.24.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
